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

时间:2009-06-30 13:53:53

标签: javascript browser dom-events

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

.watch - 监视属性被赋值并在发生时运行函数 .unwatch - 使用监视方法删除监视点集。

样本用法:

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,从未使用过这些功能 有人可以抛出一些好的用例,这些功能会派上用场吗?

7 个答案:

答案 0 :(得分:8)

真正设计的手表是属性值的验证。例如,您可以验证某些内容是否为整数:

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

答案 1 :(得分:8)

查看Object.definePropertyObject.prototype.\__defineGetter__(或\__defineSetter__)查看此功能的发展方向。

Object.defineProperty现在应该可以在所有现代浏览器中使用。

答案 2 :(得分:5)

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

今天,您现在可以使用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对象的唯一缺点是:

  1. Proxy对象在较旧的浏览器(例如IE11)中不可用,并且polyfill无法完全复制Proxy功能。
  2. 代理对象在特殊对象(例如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}}]

答案 3 :(得分:1)

您可以查看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);
    }
})

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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 :(得分:0)

这是一个简单的替代方法,仅使用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;