notifier.performChange实际上做了什么?

4 javascript ecmascript-harmony object.observe

我想了解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
  });
}
Run Code Online (Sandbox Code Playgroud)

我不明白的是,这与简单地执行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
  });
}
Run Code Online (Sandbox Code Playgroud)

我已经看到,在最新的规范中,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};  
});
Run Code Online (Sandbox Code Playgroud)

这消除了notifier.notify原始代码中对以下内容的需求,但是,这仍然是除了糖之外的其他东西,或者它之间是否存在功能差异,只是自己进行更改并发出通知?

Pio*_*oul 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
    });
}
Run Code Online (Sandbox Code Playgroud)

在第一个例子中:

  • 根据performChange()行为,回调函数内对象属性的更改将保持静默
  • 由于回调函数返回undefined,performChange()不会通知任何观察者任何其他内容
  • 但是,最后调用会notify()明确通知相应的观察者传递的更改记录

例2:

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

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

        return { incremented: amount };  
    });
}
Run Code Online (Sandbox Code Playgroud)

在第二个例子中:

  • 根据performChange()行为,回调函数内对象属性的更改将保持静默
  • 由于回调函数返回一个对象,因此performChange()将通过一个看起来与notify()示例1中显式调用得到的对象相同的对象通知相应的观察者:{ 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
Run Code Online (Sandbox Code Playgroud)

虽然这会:

(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");
})();
Run Code Online (Sandbox Code Playgroud)

原因是在内部,调用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
})();
Run Code Online (Sandbox Code Playgroud)

此外,规范中提供了很多 用法示例.