Javascript 对象中属性值更改的侦听器
Posted
技术标签:
【中文标题】Javascript 对象中属性值更改的侦听器【英文标题】:Listener for property value changes in a Javascript object 【发布时间】:2010-11-07 00:30:54 【问题描述】:浏览 javascript 文档,我发现 Javascript 对象上的以下两个函数看起来很有趣:
.watch
- 监视要分配值的属性并在发生时运行函数。.unwatch
- 删除使用 watch 方法设置的观察点。
更新:弃用警告 不要使用
watch()
和unwatch()
!这两个 方法仅在 Firefox58
之前的版本中实现,它们是 Firefox 中已弃用和删除58+
示例用法:
o = p: 1 ;
o.watch("p", function (id,oldval,newval)
console.log("o." + id + " changed from " + oldval + " to " + newval)
return newval;
);
每当我们改变“p”的属性值时,这个函数就会被触发。
o.p = 2; //logs: "o.p changed from 1 to 2"
过去几年我一直在研究 Javascript,但从未使用过这些功能。 有人可以提出一些好的用例,这些功能会派上用场吗?
【问题讨论】:
这些仅适用于基于 Gecko 的浏览器,例如 Mozilla Firefox。 Internet Explorer 通过对象公开了一个类似的方法,称为 onpropertychange。 【参考方案1】:现在是 2018 年,这个问题的答案有点过时了:
Object.watch 和 Object.observe 均已弃用,不应使用。 onPropertyChange 是一个 DOM 元素事件处理程序,仅适用于某些版本的 IE。 Object.defineProperty 允许您使对象属性不可变,这将允许您检测尝试的更改,但它也会阻止任何更改。 Defining setters and getters 有效,但需要大量设置代码,并且在您需要删除或创建新属性时效果不佳。今天,您现在可以使用Proxy 对象来监控(并拦截)对对象所做的更改。它是专为 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
)的行为并不总是如预期一样——Proxy
对象最好与普通对象或数组配对。
如果您需要观察对嵌套对象所做的更改,则需要使用专门的库,例如 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]
【讨论】:
不确定我是否理解您的代理示例。您写道,它可以拦截对目标对象的更改,但在您的示例中,您通过代理而不是目标对象修改属性值。目前尚不清楚如何使用 this 拦截对目标对象的更改。 @Johncl 这就是代理对象的工作方式:developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/… 行为是否最好用“拦截”、“虚拟化”、“陷阱”或其他词来描述,这在某种程度上取决于解释。 但这与听原帖者要求的属性变化完全不同。如果您有一个具有某些属性的对象,并且您将该对象传递到另一个黑匣子 - 但黑匣子想要侦听此对象中属性的更改并对其采取行动,那么上面的代理对象将无济于事。 @Johncl 这并没有完全不同——使用Proxy
,您可以获得完全相同的最终结果。但是,是的,您是对的,您没有观察到对目标对象直接所做的更改——名称Proxy
暗示了这一点。
@ElliotB。你怎么能听 window.test obj ?例如,当有人更改 window.test 然后控制台记录它【参考方案2】:
手表的真正设计目的是验证属性值。例如,您可以验证某物是否为整数:
obj.watch('count', function(id, oldval, newval)
var val = parseInt(newval, 10);
if(isNaN(val)) return oldval;
return val;
);
您可以使用它来验证字符串长度:
obj.watch('name', function(id, oldval, newval)
return newval.substr(0, 20);
);
但是,这些仅在最新版本的 SpiderMonkey javascript 引擎中可用。如果您正在使用 Jaxer 或嵌入 SpiderMonkey 引擎,但在您的浏览器中尚未真正可用(除非您是使用FF3)。
【讨论】:
请注意,现在可在所有现代浏览器中使用。建议:改用getter和setter。 这些方法,watch
和 unwatch
已被弃用。请不要使用它。【参考方案3】:
查看Object.defineProperty
和
Object.prototype.\__defineGetter__
(或 \__defineSetter__
)查看此功能的发展方向。
Object.defineProperty
应该很快就会在所有当代浏览器中可用。
【讨论】:
【参考方案4】:您可以查看Javascript Propery Events 库。这是一个小型库,扩展了Object.defineProperty
,带有一些事件调用者,我最近制作的。它添加了一些on[event]
属性,可以像html-Objects 的on[event]
属性一样使用。它还有一个简单的类型检查,如果失败则调用onerror
事件。
使用您的代码会产生如下结果:
var o =
Object.defineProperty(o, "p",
value:1,
writable:true,
onchange:function(e)
console.log("o." + e.target + " changed from " + e.previousValue + " to " + e.returnValue);
)
【讨论】:
【参考方案5】:Object.defineProperty
Promise
仅当目标浏览器不支持 Promise 时删除 Promise 并保留回调
重要:
1) 注意使用 promise 时的异步行为。
2) Object.defineProperty 不会触发回调,只有赋值运算符'='才会触发
Object.onPropertySet = function onPropertySet(obj, prop, ...callback_or_once)
let callback, once;
for(let arg of callback_or_once)
switch(typeof arg)
case "function": callback = arg; break;
case "boolean": once = arg; break;
let inner_value = obj[prop];
let p = new Promise(resolve => Object.defineProperty(obj, prop,
configurable: true,
// enumerable: true,
get() return inner_value; ,
set(v)
inner_value = v;
if(once)
Object.defineProperty(obj, prop,
configurable: true,
// enumerable: true,
value: v,
writable: true,
);
(callback || resolve)(v);
));
if(!callback) return p;
;
// usage
let a = ;
function sayHiValue(v) console.log(`Hi "$v"`); return v;
// do
Object.onPropertySet(a, "b", sayHiValue);
a.b = 2; // Hi "2"
a.b = 5; // Hi "5"
// or
Object.onPropertySet(a, "c", true).then(sayHiValue).then(v =>
console.log(a.c); // 4 // because a.c is set immediatly after a.c = 3
console.log(v); // 3 // very important: v != a.c if a.c is reassigned immediatly
a.c = 2; // property "c" of object "a" is re-assignable by '=' operator
console.log(a.c === 2); // true
);
a.c = 3; // Hi "3"
a.c = 4; // (Nothing)
【讨论】:
【参考方案6】:这是一个简单的替代方法,可以仅使用 getter/setter 来监视/取消监视对象字面量。每当p
属性改变时,任何函数都可以被调用。
var o =
_p: 0,
get p()
return this._p;
,
set p(p)
console.log(`Changing p from $this._p to $p`);
this._p = p;
return this._p;
o.p = 4;
o.p = 5;
【讨论】:
不会捕获array或objects的变化。前任。 -o.p = [1,2,3]
然后o.p.length = 1
此答案仅适用于对象文字,如问题所示。我不建议将其用于对象文字以外的任何东西。【参考方案7】:
你可以使用 setInterval
Object.prototype.startWatch = function (onWatch)
var self = this;
if (!self.watchTask)
self.oldValues = [];
for (var propName in self)
self.oldValues[propName] = self[propName];
self.watchTask = setInterval(function ()
for (var propName in self)
var propValue = self[propName];
if (typeof (propValue) != 'function')
var oldValue = self.oldValues[propName];
if (propValue != oldValue)
self.oldValues[propName] = propValue;
onWatch( obj: self, propName: propName, oldValue: oldValue, newValue: propValue );
, 1);
var o = a: 1, b: 2 ;
o.startWatch(function (e)
console.log("property changed: " + e.propName);
console.log("old value: " + e.oldValue);
console.log("new value: " + e.newValue);
);
【讨论】:
以上是关于Javascript 对象中属性值更改的侦听器的主要内容,如果未能解决你的问题,请参考以下文章
如何在 javascript 中更改 HTML 对象元素数据属性值
在Javascript中推送不重复的数组并更改对象的属性值?