对象.观察同步回调

Object.Observe Synchronous Callback

本文关键字:回调 同步 观察 对象      更新时间:2023-09-26

我一直在尝试使用Object。观察Chrome v36。我最初的意图是将其用于模型中的业务逻辑,但异步行为似乎使这变得不可能。我将其归结为以下示例:

function Person(name) {
   this.name = name;
   this.someOtherProperty = null;
   this.watch = function()
   {
       var self = this;
       Object.observe(this, function(changes){
           for(var i = 0; i < changes.length; i++)
           {
               if(changes[i].name == "name")
               {
                   self.someOtherProperty = changes[i].newValue;
                   console.log("Property Changed");
               }
           }
       });
   }
}
$(function () {
    var p = new Person("Alice");
    p.watch();
    p.name = "Bob";
    $("#output").text("Output: "+ p.someOtherProperty);
    console.log("Output");
});

JSFiddle链接,使用jQuery。

我的问题是"Output"在"Property Changed"之前被调用。有没有办法使对象。观察同步,还是我应该用一种更好的方法?(我正在使用AngularJS,顺便说一句)

这里的问题不是向DOM添加文本,或者输出到控制台。我的业务逻辑要求我在name更改时立即更新someOtherPropety,我更愿意将此逻辑封装在我的模型中。

显然,这只是一个示例,但是我有依赖于立即执行的业务规则。

Object.observe, "可悲的是"(阅读下一个),不执行同步任务。一旦"微任务"结束,它就会发送更改通知。

说明如下。

多年的web平台经验告诉我们,同步方法是你首先尝试的方法,因为它最容易让你理解。问题是它创建了一个从根本上危险的处理模型。如果你写代码说,更新一个对象的属性,你不希望这种情况因为更新对象的属性可能会引起一些任意代码去做它想做的事情。在你运行一个函数的过程中,让你的假设失效是不理想的。

所以,你的"微任务"在 console.log("Output")被调用后结束然后 Object.observe通知对象上的更改。

同步事件的经典方法是使用getter和setter:
Person.prototype.setName = function(name) {
    this.name = name;
    console.log("Name Changed");
};
p.setName("Bob");

当然,这将迫使您为每个想要监视的属性创建getter和setter,而忘记删除和添加新属性时的事件。

正如你所说,观察是不同步的。但你可以让watch取一个回叫然后在那里更新someOtherProperty。这样的

$(function () {
    var p = new Person("Alice");
    p.watch(function(){
        $("#output").text("Output: "+ p.someOtherProperty);
    });
    p.name = "Bob";
    console.log("Output");
});
更新jsfiddle

Object.observe同步行为是没有意义的。它将不得不阻塞线程并等待一些变化。

你应该传递一个回调到你的watch函数,以便在发生变化时执行:

this.watch = function (callback) {
    var self = this;
    Object.observe(this, function (changes) {
        changes.forEach(function (change) {
            if (change.name === 'name') {
                self.someOtherProperty = change.newValue;
                console.log("Property Changed");
                callback();
            }
        });
    });
}
$(function () {
    var p = new Person("Alice");
    p.watch(function () {
        // !!!!! OF COURCE YOU SHOULD NEVER DO IT LIKE THIS IN ANGULAR !!!! //
        $("#output").text("Output: " + p.someOtherProperty);
        console.log("Output");
    });
    p.name = "Bob";
});

顺便说一句,如果你使用的是Angular(从你的代码和摆弄来看,它一点也不明显),你不应该在意在观察到变化发生时是否执行任何代码。只要你用$scope.$apply()封装代码,Angular就会负责更新视图等。

例如:

<div ng-controller="someCtrl">
    Output: {{p.someOtherProperty}}
</div>
.controller('someCtrl', function ($scope, Person) {
    $scope.p = new Person('Alice');
    $scope.p.watch();
    $scope.p.name = 'Bob';
});
app.factory('Person', function ($rootScope) {
    return function Person(name) {
        var self = this;
        self.name = name;
        self.someOtherProperty = null;
        this.watch = function () {
            Object.observe(self, function (changes) {
                $rootScope.$apply(function () {
                    changes.forEach(function (change) {
                        console.log(change);
                        if (change.name === 'name') {
                            self.someOtherProperty = self.name;
                        }
                    });
                });
            });
        };
    };
});

请参见这个简短的Angular演示


更好的是,看看这个更"真实"的演示
基本上,使用O.o代替Angular的脏检查的好处是,你节省了$$watchers的时间,因此你的$digest周期更快,成本更低。
当ES6发布时,Angular也会使用这个机制(O.o)。