是否有可能在JS中有一个事件,当某个变量的值发生变化时触发?JQuery被接受。
当前回答
这个问题最初发布于2009年,现有的大多数答案要么过时,无效,要么需要包含庞大的库:
对象。观看和反对。Observe都已弃用,不应该使用。 onPropertyChange是一个DOM元素事件处理程序,只在某些版本的IE中工作。 object . defineproperty允许您将对象属性设置为不可变的,这将允许您检测尝试的更改,但它也将阻止任何更改。 定义setter和getter是可行的,但是它需要大量的设置代码,并且当您需要删除或创建新属性时,它不能很好地工作。
截至2018年,您现在可以使用代理对象来监视(和拦截)对对象的更改。它是专门为OP试图做的事情而建造的。这里有一个基本的例子:
var targetObj = {};
var targetProxy = new Proxy(targetObj, {
set: function (target, key, value) {
console.log(`${key} set to ${value}`);
target[key] = value;
return true;
}
});
targetProxy.hello_world = "test"; // console: 'hello_world set to test'
Proxy对象唯一的缺点是:
Proxy对象在旧的浏览器(如IE11)中不可用,而且polyfill不能完全复制Proxy功能。 代理对象对于特殊对象(例如Date)并不总是表现得像预期的那样——代理对象最好与普通对象或数组配对。
如果你需要观察对嵌套对象所做的更改,那么你需要使用一个专门的库,如Observable Slim(我已经发布了)。它是这样工作的:
var test = {testing:{}};
var p = ObservableSlim.create(test, true, function(changes) {
console.log(JSON.stringify(changes));
});
p.testing.blah = 42; // console: [{"type":"add","target":{"blah":42},"property":"blah","newValue":42,"currentPath":"testing.blah",jsonPointer:"/testing/blah","proxy":{"blah":42}}]
其他回答
No.
但是,如果这真的很重要,你有两个选择(第一个是测试,第二个不是):
首先,使用setter和getter,如下所示:
var myobj = {a : 1};
function create_gets_sets(obj) { // make this a framework/global function
var proxy = {}
for ( var i in obj ) {
if (obj.hasOwnProperty(i)) {
var k = i;
proxy["set_"+i] = function (val) { this[k] = val; };
proxy["get_"+i] = function () { return this[k]; };
}
}
for (var i in proxy) {
if (proxy.hasOwnProperty(i)) {
obj[i] = proxy[i];
}
}
}
create_gets_sets(myobj);
然后你可以这样做:
function listen_to(obj, prop, handler) {
var current_setter = obj["set_" + prop];
var old_val = obj["get_" + prop]();
obj["set_" + prop] = function(val) { current_setter.apply(obj, [old_val, val]); handler(val));
}
然后像这样设置监听器:
listen_to(myobj, "a", function(oldval, newval) {
alert("old : " + oldval + " new : " + newval);
}
其次,你可以把手表的价值:
给定上面的myobj,上面有'a':
function watch(obj, prop, handler) { // make this a framework/global function
var currval = obj[prop];
function callback() {
if (obj[prop] != currval) {
var temp = currval;
currval = obj[prop];
handler(temp, currval);
}
}
return callback;
}
var myhandler = function (oldval, newval) {
//do something
};
var intervalH = setInterval(watch(myobj, "a", myhandler), 100);
myobj.set_a(2);
这个问题最初发布于2009年,现有的大多数答案要么过时,无效,要么需要包含庞大的库:
对象。观看和反对。Observe都已弃用,不应该使用。 onPropertyChange是一个DOM元素事件处理程序,只在某些版本的IE中工作。 object . defineproperty允许您将对象属性设置为不可变的,这将允许您检测尝试的更改,但它也将阻止任何更改。 定义setter和getter是可行的,但是它需要大量的设置代码,并且当您需要删除或创建新属性时,它不能很好地工作。
截至2018年,您现在可以使用代理对象来监视(和拦截)对对象的更改。它是专门为OP试图做的事情而建造的。这里有一个基本的例子:
var targetObj = {};
var targetProxy = new Proxy(targetObj, {
set: function (target, key, value) {
console.log(`${key} set to ${value}`);
target[key] = value;
return true;
}
});
targetProxy.hello_world = "test"; // console: 'hello_world set to test'
Proxy对象唯一的缺点是:
Proxy对象在旧的浏览器(如IE11)中不可用,而且polyfill不能完全复制Proxy功能。 代理对象对于特殊对象(例如Date)并不总是表现得像预期的那样——代理对象最好与普通对象或数组配对。
如果你需要观察对嵌套对象所做的更改,那么你需要使用一个专门的库,如Observable Slim(我已经发布了)。它是这样工作的:
var test = {testing:{}};
var p = ObservableSlim.create(test, true, function(changes) {
console.log(JSON.stringify(changes));
});
p.testing.blah = 42; // console: [{"type":"add","target":{"blah":42},"property":"blah","newValue":42,"currentPath":"testing.blah",jsonPointer:"/testing/blah","proxy":{"blah":42}}]
这是一个老线程,但我在使用Angular寻找解决方案时偶然发现了第二高的答案(自定义侦听器)。虽然这个解决方案是可行的,但angular有一个更好的内置方法来解决这个问题,使用@Output和事件发射器。在自定义监听器回答中的例子:
ChildComponent.html
<button (click)="increment(1)">Increment</button>
ChildComponent.ts
import {EventEmitter, Output } from '@angular/core';
@Output() myEmitter: EventEmitter<number> = new EventEmitter<number>();
private myValue: number = 0;
public increment(n: number){
this.myValue += n;
// Send a change event to the emitter
this.myEmitter.emit(this.myValue);
}
ParentComponent.html
<child-component (myEmitter)="monitorChanges($event)"></child-component>
<br/>
<label>{{n}}</label>
ParentComponent.ts
public n: number = 0;
public monitorChanges(n: number){
this.n = n;
console.log(n);
}
现在每次单击子按钮时都会更新非父按钮。工作stackblitz
不是直接的:你需要一个“addListener/removeListener”接口的一对getter/setter…或者一个NPAPI插件(但这完全是另一个故事)。
@akira和@mo-d-genesis的解决方案可以进一步简化,因为在这个例子中DOM操作不依赖于状态:
CodePen
const render = (val) => {
document.getElementById("numberDiv").innerHTML = val;
};
state = {
_value_internal: undefined,
set value(val) {
// 1. set state value
this._value_internal = val;
// 2. render user interface
render(val);
},
get value() {
return this._value_internal;
},
};
const onClick = () => {
state.value = state.value + 1; // state change leads to re-render!
};
// set default value
state.value = 0;
对应的html:
<div id="numberDiv"></div>
<button onclick="onClick()">
Click to rerender
</button>
备注:
我重命名了变量和函数,以更好地反映它们的语义。 仅供参考:通过改变变量,Svelte提供了非常类似的反应行为