【问题标题】:Why does Object.observe() not provide the data path of change to a callback?为什么 Object.observe() 不给回调提供变化的数据路径?
【发布时间】:2014-11-06 15:47:52
【问题描述】:

Object.observe() 回调的更改数组包含具有以下四个属性的对象:

  • 姓名
  • 对象
  • 类型
  • 旧值

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/observe#Parameters

为什么本地没有提供path?示例:

var ob = {
    foo: [
        {moo: "bar", val: 5},
        {val: 8}
    ]
}

ob.foo[0].val = 1;
// callback should provide path "foo.0.val" or "foo[0].val"

有一个 Node.js 模块扩展了 Object.observe() 以包含路径:observed.js
但我担心原生observe() 的性能提升会丢失(如果没有,您能否解释一下它是如何实现的?)。 browserify 模块可能是可能的,但无法想象它会在同步环境中表现良好,我仍然想知道为什么似乎没有人考虑额外的 path 属性。

【问题讨论】:

    标签: javascript observers ecmascript-harmony object.observe


    【解决方案1】:

    因为没有明确的道路。

    考虑以下几点:

    var movall = {moo: "bar", val: 5};
    var ob1    = {a: mooval};
    var ob2    = {b: movall};
    

    现在假设我观察到movall。然后我更新moo。路径是什么?是movall.moo,还是ob1.a.moo,还是ob2.b.moo?如果我观察ob1,则没有报告任何更改,因为它的任何属性都没有更改(更改是内部对其属性之一,这不算数)。

    对象独立于嵌套在其他对象中的存在。它们可以嵌套在多个其他对象中。没有唯一的“路径”来描述如何从可能的多个起点到可能已更改的特定属性。

    JS 也不知道您到达被更改属性的路径。所以在ob.foo[0].val = 1; 中,JS 只是评估链,到达foo[0] 对象,更改它的val 属性,此时不知道它是如何到达foo[0] 的。它只知道foo[0] 已经改变。它在 ob 中发生了变化,但它也可能在恰好具有 foo[0] 作为属性的其他对象中发生了变化。

    但是,您可以通过在低级观察/通知机制之上构建一些机制来实现您似乎想要实现的目标。我们将在对象上定义一个函数,该函数在其属性对象上设置观察者,以此类推,并使用正确构造的路径将更改记录传播回来:

    function notifySubobjectChanges(object) {
      var notifier = Object.getNotifier(object);        // get notifier for this object
      for (var k in object) {                           // loop over its properties
        var prop = object[k];                           // get property value
        if (!prop || typeof prop !== 'object') break;   // skip over non-objects
        Object.observe(prop, function(changes) {        // observe the property value
          changes.forEach(function(change) {            // and for each change
            notifier.notify({                           // notify parent object
              object: change.object,                    // with a modified changerec
              name: change.name,                        // which is basically the same
              type: change.type, 
              oldValue: change.oldValue, 
              path: k + 
                (change.path ? '.' + change.path : '')  // but has an addt'l path property
            });
          });
        });
        notifySubobjectChanges(prop);                   // repeat for sub-subproperties
      }
    }
    

    (注意:change 对象已冻结,我们无法向其中添加任何内容,因此我们必须复制它。)

    现在

    a = { a: { b: {c: 1 } } };                     // nested objects
    notifySubobjectChanges(a);                     // set up recursive observers
    Object.observe(a, console.log.bind(console));  // log changes to console
    a.a.b.c = 99;
    
    >> 0: Object
      name: "c"
      object: Object
      oldValue: 1
      path: "a.b"                                  // <=== here is your path!
      type: "update"
    

    以上代码不是生产质量,使用风险自负。

    【讨论】:

    • 好点,但Object.observe() 在您描述的确切情况下表现如何?如果另外两个对象包含第一个对象,会发生 3 个更改事件吗?新对象被传递给回调,因此继承/对象嵌套不会成为问题。顺便说一句:path 不应包含 movallobob2,但只能包含以下内容。结合传入的对象,更改typeoldValue,就可以直接创建OT操作了。
    • 变化事件只有在观察到时才会“发生”,就像一棵树倒在森林里一样。因此,如果我们还观察到了ob2,那么是的,另一个更改事件将发送给该​​观察者。
    • 澄清一下:如果我们观察到movallob2ob2 包含movall,我将更改为ob2ob2.b.movall.moo = "foo" — 会有一个变化事件到movall 和一个到ob2?如果是,则不存在路径歧义,因为它与我们观察到的对象相关,而与嵌套对象无关(moo 用于movallb.movall.moo 用于ob2)。
    • 如果我理解您的评论正确,那么在没有额外机制的情况下,对ob2.movall.moo 的更改不是ob2 的更改。仅在包含该属性的对象上报告更改。对ob2.movall.moo 的更改不是ob2 的任何属性的更改,而是对其中一个属性值的内部的更改。
    • 我明白了,尽管这很有意义,但在这种情况下,我不需要任何额外的机器来获取 ob2 的事件。但是,我不明白到movall 的观察者回调的更改事件中怎么可能存在路径歧义。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-09-05
    • 2023-03-19
    相关资源
    最近更新 更多