notifier.performChange实际上做了什么?

时间:2014-11-25 05:19:49

标签: javascript ecmascript-harmony object.observe

我想了解Object.getNotifier(object).performChange。从概念上讲,我理解它是专门用于定义"宏"或更高级别的变化。来自example everyone seems to refer to

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了,但是,这是否不是糖,或者它之间是否有功能差异,只是自己进行更改并发出通知?< / p>

2 个答案:

答案 0 :(得分:5)

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

以下是您需要了解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()的行为,对回调函数内对象属性的更改将保持静默
  • 由于回调函数返回一个对象,performChange()将通知一个对象,该对象看起来与示例1中显式调用notify()得到的对象相同:{ object: Object, type: Thingy.INCREMENT, increment: amount } < / LI>

这两个示例应涵盖您希望使用performChange()的大多数情况,以及如何使用它。我会继续潜水,因为这种野兽的行为非常有趣。


异步性

观察者以异步方式执行。这意味着上面示例中increment()函数内发生的所有内容实际上都会在increment()执行完后向观察者报告 - 并且只有这样。

换句话说,所有这些:

  • performChange()
  • 之外更改观察对象的属性
  • performChange()的回调
  • 中返回对象
  • 致电notify()

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

同步变更交付

如果您需要了解increment()执行期间的待处理更改(待处理的更改=将在increment()结束时向观察员报告的所有更改,但是避难所&#但是,还有一个解决方案:Object.deliverChangeRecords(callback)

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

换句话说,这不会起作用:

&#13;
&#13;
(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
&#13;
&#13;
&#13;

虽然这会:

&#13;
&#13;
(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");
})();
&#13;
&#13;
&#13;

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

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

这正是Object.deliverChangeRecords(callback)的用途:它会对callback进行所有挂起的更改,并通过将所有这些更改传递给该回调来执行。

这解释了为什么deliverChangeRecords()只需要一个参数,即回调的参数。如下面的示例所示,将回调传递给deliverChangeRecords()将使用所有其挂起的更改(包括来自多个对象的更改)执行该回调。这与回调的一般行为一致,可能是异步调用或通过deliverChangeRecords()调用。

&#13;
&#13;
(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
})();
&#13;
&#13;
&#13;

此外,规范中还提供了好的 usage examples

答案 1 :(得分: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