为什么不推荐使用Object.observe()

Sha*_*had 47 javascript

有替代方法吗?

还有另一种方法可以在对象中进行更改检测吗?

有代理方法,但任何人都可以告诉我如何使用代理实现这一点:

var obj = {
  foo: 0,
  bar: 1
};

Object.observe(obj, function(changes) {
  console.log(changes);
});

obj.baz = 2;
// [{name: 'baz', object: <obj>, type: 'add'}]

obj.foo = 'hello';
// [{name: 'foo', object: <obj>, type: 'update', oldValue: 0}]
Run Code Online (Sandbox Code Playgroud)

Dan*_*nce 51

你可以通过getter和setter来实现这一点.

var obj = {
  get foo() {
    console.log({ name: 'foo', object: obj, type: 'get' });
    return obj._foo;
  },
  set bar(val) {
    console.log({ name: 'bar', object: obj, type: 'set', oldValue: obj._bar });
    return obj._bar = val;
  }
};

obj.bar = 2;
// {name: 'bar', object: <obj>, type: 'set', oldValue: undefined}

obj.foo;
// {name: 'foo', object: <obj>, type: 'get'}
Run Code Online (Sandbox Code Playgroud)

或者,在支持Proxies的浏览器中,您可以编写更通用的解决方案.

var obj = {
  foo: 1,
  bar: 2
};

var proxied = new Proxy(obj, {
  get: function(target, prop) {
    console.log({ type: 'get', target, prop });
    return Reflect.get(target, prop);
  },
  set: function(target, prop, value) {
    console.log({ type: 'set', target, prop, value });
    return Reflect.set(target, prop, value);
  }
});

proxied.bar = 2;
// {type: 'set', target: <obj>, prop: 'bar', value: 2}

proxied.foo;
// {type: 'get', target: <obj>, prop: 'bar'}
Run Code Online (Sandbox Code Playgroud)

  • 使用对象访问的@Emissary将再次触发代理陷阱,从而导致无限循环. (4认同)
  • 谢谢,但我无法用 `target[prop] = value` 复制无限循环 - 你在什么环境中运行?代理也不应该替换原始变量以“观察”,否则`obj.bar = x` 不执行此处建议的操作。 (2认同)
  • 可能与不完整的代理支持有关。对此[此处]有更多深入的讨论(http://stackoverflow.com/questions/25421903/what-does-the-reflect-object-do-in-javascript)。 (2认同)
  • @DanPrince `Reflect.get` 确实会触发 get 陷阱。[MDN](https://beta.developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy/handler/get#Interceptions)。 (2认同)
  • @Mason,理解你的观点很重要,所以我将我的两分钱放在这里:对象访问或“Reflect.get” - 如果在 __proxy__ 上运行,则 __will__ 都会触发陷阱,如果针对 __target__ 运行,则 __none__ 不会被捕获目的。因此,这里重要的一点是,应该在陷阱内处理“目标”,使用哪种访问方法并不重要。 (2认同)

Nir*_*rus 13

@Dan Prince解决方案是正确的.它应该是第一选择.

如果你想支持相当旧的浏览器,我建议你去polyfill或使用IE 9支持的Object.defineProperties API.

var obj = Object.defineProperties({}, {
    "foo":{
        get:function(){
            console.log("Get:"+this.value);
        },
        set:function(val){
            console.log("Set:"+val);
            this.value = val;
        }
    },

    "bar":{         
        get:function(){
            console.log("Get:"+this.value);
        },
        set:function(val){
            console.log("Set:"+val);
            this.value = val;
        }
    }
 });
Run Code Online (Sandbox Code Playgroud)


Gul*_*rYA 13

免责声明:我是下面建议的对象观察者库的作者.

我不会选择getter/setter解决方案 - 它很复杂,不可扩展且无法维护.Backbone以这种方式进行双向绑定,并且使其正常工作的样板是相当一部分代码.

代理是实现所需内容的最佳方式,只需在上面的示例中添加一些回调注册和管理,并在更改时执行它们.

关于polyfill库:其中一些/大多数使用"脏检查"或轮询技术实现 - 效率低,性能不高.偶尔,这是上面Nirus指出的polyfill的情况.

我建议选择一些通过Proxies进行观察的库.有一些,对象观察者就是其中之一:为这个用例准确编写,利用原生代理,提供深层树观察等.

  • :)自己是公司员工 - 你正在打开一扇门!好吧,这个项目是一种表达自己的愿望+我自己的帮助我自己的网络应用程序+表达极端不喜欢任何像Angular <any_version>/React和其他人.我现在用它来编写东西,将它与CustomElements大量集成并享受新世界.没有IE,无论好坏. (3认同)
  • 是的,这个实现不会在任何不支持 Proxy 对象的环境中运行。然而,所有可下载的主要浏览器(Chrome、Firefox、Opera)都已经存在,实际上 Edge 也是如此。手机版也有。所以我能看到的唯一问题是 IE pre-Edge - 好吧,我个人已经把它抛在了后面。 (2认同)