为什么Object.obsoe()没有为回调提供更改的数据路径

Why does Object.observe() not provide the data path of change to a callback?

本文关键字:数据路径 回调 obsoe Object 为什么      更新时间:2023-09-26

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

  • 名称
  • 对象
  • 类型
  • oldValue

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()的性能增益会丢失(如果没有,你能解释一下它是如何实现的吗?)。可能可以浏览该模块,但无法想象它在同步环境中会表现良好,我仍然想知道为什么没有人考虑额外的path属性。

因为没有明确的路径。

考虑以下内容:

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

现在假设我观察movall。然后我更新moo。路径是什么?是movall.mooob1.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"

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