Object.observe(..)

前端web开发的圣杯之一就是数据绑定 —— 监听一个数据对象的更新并同步这个数据的DOM表现形式。大多数JS框架都为这些类型的操作提供某种机制。

在ES6后期,我们似乎很有可能看到这门语言通过一个称为Object.observe(..)的工具,对此提供直接的支持。实质上,它的思想是你可以建立监听器来监听一个对象的变化,并在一个变化发生的任何时候调用一个回调。例如,你可相应地更新DOM。

你可以监听六种类型的变化:

  • add
  • update
  • delete
  • reconfigure
  • setPrototype
  • preventExtensions

默认情况下,你将会收到所有这些类型的变化的通知,但是你可以将它们过滤为你关心的那一些。

考虑如下代码:

var obj = { a: 1, b: 2 };

Object.observe(
    obj,
    function(changes){
        for (var change of changes) {
            console.log( change );
        }
    },
    [ "add", "update", "delete" ]
);

obj.c = 3;
// { name: "c", object: obj, type: "add" }

obj.a = 42;
// { name: "a", object: obj, type: "update", oldValue: 1 }

delete obj.b;
// { name: "b", object: obj, type: "delete", oldValue: 2 }

除了主要的"add""update"、和"delete"变化类型:

  • "reconfigure"变化事件在对象的一个属性通过Object.defineProperty(..)而重新配置时触发,比如改变它的writable属性。更多信息参见本系列的 this与对象原型
  • "preventExtensions"变化事件在对象通过Object.preventExtensions(..)被设置为不可扩展时触发。

    因为Object.seal(..)Object.freeze(..)两者都暗示着Object.preventExtensions(..),所以它们也将触发相应的变化事件。另外,"reconfigure"变化事件也会为对象上的每个属性被触发。

  • "setPrototype"变化事件在一个对象的[[Prototype]]被改变时触发,不论是使用__proto__setter,还是使用Object.setPrototypeOf(..)设置它。

注意,这些变化事件在会在变化发生后立即触发。不要将它们与代理(见第七章)搞混,代理是可以在动作发生之前拦截它们的。对象监听让你在变化(或一组变化)发生之后进行应答。

自定义变化事件

除了六种内建的变化事件类型,你还可以监听并触发自定义变化事件。

考虑如下代码:

function observer(changes){
    for (var change of changes) {
        if (change.type == "recalc") {
            change.object.c =
                change.object.oldValue +
                change.object.a +
                change.object.b;
        }
    }
}

function changeObj(a,b) {
    var notifier = Object.getNotifier( obj );

    obj.a = a * 2;
    obj.b = b * 3;

    // queue up change events into a set
    notifier.notify( {
        type: "recalc",
        name: "c",
        oldValue: obj.c
    } );
}

var obj = { a: 1, b: 2, c: 3 };

Object.observe(
    obj,
    observer,
    ["recalc"]
);

changeObj( 3, 11 );

obj.a;            // 12
obj.b;            // 30
obj.c;            // 3

变化的集合("recalc"自定义事件)为了投递给监听器而被排队,但还没被投递,这就是为什么obj.c依然是3

默认情况下,这些变化将在当前事件轮询(参见本系列的 异步与性能)的末尾被投递。如果你想要立即投递它们,使用Object.deliverChangeRecords(observer)。一旦这些变化投递完成,你就可以观察到obj.c如预期地更新为:

obj.c;            // 42

在前面的例子中,我们使用变化完成事件的记录调用了notifier.notify(..)。将变化事件的记录进行排队的一种替代形式是使用performChange(..),它把事件的类型与事件记录的属性(通过一个函数回调)分割开来。考虑如下代码:

notifier.performChange( "recalc", function(){
    return {
        name: "c",
        // `this` 是被监听的对象
        oldValue: this.c
    };
} );

在特定的环境下,这种关注点分离可能与你的使用模式匹配的更干净。

中止监听

正如普通的事件监听器一样,你可能希望停止监听一个对象的变化事件。为此,你可以使用Object.unobserve(..)

举例来说:

var obj = { a: 1, b: 2 };

Object.observe( obj, function observer(changes) {
    for (var change of changes) {
        if (change.type == "setPrototype") {
            Object.unobserve(
                change.object, observer
            );
            break;
        }
    }
} );

在这个小例子中,我们监听变化事件直到我们看到"setPrototype"事件到来,那时我们就不再监听任何变化事件了。

results matching ""

    No results matching ""