Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/472.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript对象中属性值更改的侦听器_Javascript_Browser_Dom Events - Fatal编程技术网

Javascript对象中属性值更改的侦听器

Javascript对象中属性值更改的侦听器,javascript,browser,dom-events,Javascript,Browser,Dom Events,浏览Javascript文档时,我发现Javascript对象上的以下两个函数看起来很有趣: -监视要分配值的属性,并在发生此情况时运行函数。 .unwatch-删除使用watch方法设置的观察点 更新:弃用警告 不要使用watch()和unwatch()!这两个 方法仅在Firefox版本58之前的版本中实现,它们是 已在Firefox中弃用并删除58+ 示例用法: o = { p: 1 }; o.watch("p", function (id,oldval,newva

浏览Javascript文档时,我发现Javascript对象上的以下两个函数看起来很有趣:

-监视要分配值的属性,并在发生此情况时运行函数。
.unwatch
-删除使用watch方法设置的观察点


更新弃用警告
不要使用
watch()
unwatch()
!这两个 方法仅在Firefox版本
58
之前的版本中实现,它们是 已在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,从未使用过这些函数。

有人能提供一些好的用例,让这些功能派上用场吗?

手表的真正用途是验证属性值。例如,您可以验证某物是否为整数:

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)。

签出并 (或
\\uuu定义设置器
)查看此功能的发展方向


Object.defineProperty
应该很快就能在所有当代浏览器中使用。

你可以看看这个库。这是一个小型库,扩展了我最近制作的带有一些事件调用程序的
Object.defineProperty
。它在[event]
属性上添加了一些
,这些属性可以像HTML对象的[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);
    }
})
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}}]

您可以使用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);
});

现在是2018年,这个问题的答案有点过时:

  • 和都已弃用,不应使用
  • 是一个DOM元素事件处理程序,仅在某些IE版本中有效
  • 允许您使对象属性不可变,这将允许您检测尝试的更改,但也会阻止任何更改
  • 可以,但它需要大量的设置代码,并且在需要删除或创建新属性时无法正常工作
现在,您可以使用对象来监视(和截获)对对象所做的更改。它是专为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'
代理
对象的唯一缺点是:

  • 代理
    对象在旧浏览器(如IE11)中不可用,并且无法完全复制
    代理
    功能
  • 对于特殊对象(例如,
    Date
    ),
    代理对象的行为并不总是如预期的那样-
    代理对象最好与普通对象或数组配对
  • 如果需要观察对嵌套对象所做的更改,则需要使用专门的库,如(我编写的)。它的工作原理如下:

    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);
        }
    })
    
    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}}]
    

    仅当目标浏览器不支持承诺时,才删除承诺并保留回调

    重要: 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)
    

    这里有一个简单的替代方法,可以仅使用getter/setter来监视/取消跟踪对象文字。无论何时更改
    p
    属性,都可以调用任何函数

    var o={
    _p:0,,
    得到p(){
    把这个还给我;
    },
    集p(p){
    log(`将p从${this.\u p}更改为${p}`);
    这个;
    把这个还给我;
    }
    }
    o、 p=4;
    
    o、 p=5这些仅适用于基于Gecko的浏览器,如Mozilla Firefox。Internet Explorer在对象上公开了一个类似的方法,名为onpropertychange.Heads,现在可以在所有现代浏览器上使用。建议:改用getter和setter。不推荐使用这些方法,
    watch
    unwatch
    。请不要使用它。我不确定我是否理解您的代理示例。您写道,它可以拦截对目标对象的更改,但在您的示例中,您通过代理而不是目标对象修改属性值。现在还不清楚如何用这个截取对目标对象的更改。@Johncl代理对象就是这样工作的:行为是否最好用“截取”、“虚拟化”、“陷阱”来描述或者其他一些事情需要解释,但这与倾听原始海报要求的财产变化完全不同。如果您有一个具有某些属性的对象,并将该对象传递到另一个黑匣子中,但黑匣子希望侦听此对象中某个属性的更改并对其执行操作,则上面的代理对象将毫无帮助。@Johncl这并不是完全不同——使用
    代理
    可以获得完全相同的最终结果。但是,是的,您是正确的,您没有观察到直接对目标对象所做的更改——这是由名称
    Proxy
    @ElliotB所暗示的。如何收听window.test obj?例如,当有人更改window.test时,控制台日志不会捕获数组或对象的更改。例如-
    o.p=[1,2,3]
    然后
    o.p.length=1
    此答案仅适用于对象文本,如问题所示。我不建议将其用于对象文字以外的任何其他内容。