4

我试图理解Object.getNotifier(object).performChange。从概念上讲,我理解它是为定义“宏”或更高级别的更改而设计的。从示例中,每个人似乎都指的是

increment: function(amount) {
  var notifier = Object.getNotifier(this);

  notifier.performChange(Thingy.INCREMENT, function() {
    this.a += amount;
    this.b += amount;
  }, this);

  notifier.notify({
    object: this,
    type: Thingy.INCREMENT,
    incremented: amount
  });
}

我不明白的是,这与简单地执行notifier.performChange直接传递给的匿名函数而不是作为回调有什么不同?换句话说,它与以下内容有何不同:

increment: function(amount) {
  var notifier = Object.getNotifier(this);

  this.a += amount;
  this.b += amount;

  notifier.notify({
    object: this,
    type: Thingy.INCREMENT,
    incremented: amount
  });
}

我已经看到,在最新的规范中,notifier.performChange可能会返回一个对象,然后将其作为通知发出,如下所示:

notifier.performChange(Thing.INCREMENT, function() {
    this.a += amount;
    this.b += amount;

    // a notification is issues with this return value,
    // including the type passed to notifier.performChange,
    // and the object underlying notifier. 
    return {incremented: amount};  
});

这消除了notifier.notify原始代码中对以下内容的需求,但是,这是否不是糖,或者这与仅进行更改并自己发出通知之间是否存在功能差异?

4

2 回答 2

5

我想为这个我也问自己的问题提供一个明确的答案,所以我查看了Object.observe 规范

以下是您需要了解的内容Object.getNotifier(obj).performChange(changeType, changeFn)

  • 它运行changeFn
  • changeFn运行时,它故意不通知任何观察者可能发生在属性上的更改obj
  • 您可以changeFn返回一个对象:obj的观察者将收到该对象自己的属性的通知

亲自查看,%NotifierPrototype%.performChange(changeType, changeFn)是您在规范中寻找的内容。

应用于您的示例,这意味着这两个导致完全相同的结果,但做事略有不同:

示例 1:

increment: function(amount) {
    var notifier = Object.getNotifier(this);

    notifier.performChange(Thingy.INCREMENT, function() {
        this.a += amount;
        this.b += amount;
    });

    notifier.notify({
        object: this,
        type: Thingy.INCREMENT,
        incremented: amount
    });
}

在第一个示例中:

  • 根据performChange()的行为,回调函数内对对象属性的更改将保持沉默
  • 由于回调函数返回undefinedperformChange()不会通知任何观察者其他任何事情
  • 但是,最后调用notify()显式通知适当的观察者,并传递了更改记录

示例 2:

increment: function(amount) {
    var notifier = Object.getNotifier(this);

    notifier.performChange(Thingy.INCREMENT, function() {
        this.a += amount;
        this.b += amount;

        return { incremented: amount };  
    });
}

在第二个示例中:

  • 根据performChange()的行为,回调函数内对对象属性的更改将保持沉默
  • 由于回调函数返回一个对象,因此将通知适当的观察者一个与示例 1 中performChange()显式调用所产生的对象相同的对象:notify(){ object: Object, type: Thingy.INCREMENT, increment: amount }

这两个示例应该涵盖您想要使用的大多数情况performChange(),以及如何使用它。不过我会继续潜水,因为这只野兽的行为很有趣。


异步性

观察者是异步执行的。这意味着increment()上面示例中函数内部发生的所有事情实际上都会在执行完毕后报告increment()给观察者——而且只有到那时。

换句话说,所有这些:

  • 对观察对象的属性进行更改performChange()
  • performChange()在的回调中返回一个对象
  • 打电话notify()

increment()只有在完成运行后才会通知适当的观察者。

同步变更交付

如果您需要了解increment()执行期间的待处理更改(待处理更改 = 将在 结束时报告给观察者increment()但尚未报告的所有更改),有一个解决方案:Object.deliverChangeRecords(callback).

请注意,尽管这callback需要引用您之前已经注册为该对象的观察回调的函数。

换句话说,这是行不通的:

(function() {
    var obj = { prop: "a" };

    Object.observe(obj, function(changes) {
        console.log(changes);
    });
    
    obj.prop = "b";

    Object.deliverChangeRecords(function(changes) {
        console.log(changes);
    });

    console.log("End of execution");
})(); // Meh, we're notified of changes here, which isn't what we wanted

虽然这将:

(function() {
    var obj = { prop: "a" },

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

    obj.prop = "b";

    Object.deliverChangeRecords(callback); // Notified of changes here, synchronously: yay!

    console.log("End of execution");
})();

这样做的原因是,在内部,调用Object.observe(obj, callback)一个对象obj会将传递的callback函数添加到的观察回调列表(在规范中obj称为)。[[ChangeObservers]]这些回调中的每一个都只会针对特定类型的更改(第三个Object.observe()参数)执行,如果没有传递参数,则将执行所有默认更改。(这是一个重要的细节,因为这意味着如果您想使用type更改的自定义,您需要将其显式传递给Object.observe()的第三个参数,否则您将不会收到有关该类型的任何更改的通知。)

此外,每个待处理的更改都将在内部添加到每个匹配的观察回调队列中。这意味着每个观察回调都有自己的一组未决更改。

这正是Object.deliverChangeRecords(callback)它的用途:它接受所有待处理的更改callback并通过传递所有这些更改来执行该回调。

这就解释了为什么deliverChangeRecords()只需要一个参数,即回调的参数。如下例所示,将回调传递给deliverChangeRecords()将执行该回调及其所有未决更改,包括来自多个对象的更改。这符合回调的一般行为,可能是异步调用,也可能是通过deliverChangeRecords().

(function() {
    var obj1 = { prop1: "a" },
        obj2 = { prop2: "a" },

        commonCallback = function(changes) {
            console.log(changes);
        };
    
    Object.observe(obj1, commonCallback);
    Object.observe(obj2, commonCallback);

    obj1.prop1 = "b";
    obj2.prop2 = "b";

    Object.deliverChangeRecords(commonCallback); // Notified of the changes to both obj1.prop1 and obj2.prop2
})();

此外,规范中提供了很好的 使用示例。

于 2014-12-18T21:10:36.763 回答
4

经过一个小时的大量测试,我终于弄明白了。我有同样的问题(有什么performChange用?),也有同样的想法,只是把它拿下来打电话

this.a += amount;
this.b += amount;

然而:重点notifier.performChange是让观察者不会观察到每一个变化。

我是这样测试的:

var obj = {
  x: 5,
  y: 10
};

function noti() {
  console.log('noti start');
  var notifier = Object.getNotifier(obj);

  notifier.performChange('ok', function() {
    obj.x++;
    obj.y++;
  });

  notifier.notify({
    type: 'ok',
    oldValue: 5
  });
  console.log('noti end');
};

function noti2() {
  console.log('noti2 start');
  var notifier = Object.getNotifier(obj);

  obj.x++;
  obj.y++;

  notifier.notify({
    type: 'ok',
    oldValue: 5
  });
  console.log('noti2 end');
};

function observer(changes) {
  for (var change of changes) {
    console.log('observer: change =', change, ' newValue=', change.object[change.name]);
  }
};

Object.observe(obj, observer, ['ok', 'update']);

console.log('calling noti2()');
noti2(); //will log the changes of update twice becuase of the x and y property of obj

// add delay manually because observer calls are asynchronous and
// we want to clearly separate the notification function calls in our logs
setTimeout(function() {
  console.log('calling noti()');

  noti(); //will only log the ok type. that's what they mean by big change
          //so everything you do inside the performChange won't be observed
}, 100);

它应该返回以下控制台输出:

calling noti2()
noti2 start
noti2 end
observer: change = Object {type: "update", object: Object, name: "x", oldValue: 5}  newValue= 6
observer: change = Object {type: "update", object: Object, name: "y", oldValue: 10}  newValue= 11
observer: change = Object {object: Object, type: "ok", oldValue: 5}  newValue= undefined

calling noti()
noti start
noti end
observer: change = Object {object: Object, type: "ok", oldValue: 5}  newValue= undefined
于 2014-12-01T02:15:26.493 回答