Object.Observe同步回调

时间:2014-07-31 09:38:31

标签: javascript angularjs object.observe

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

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。

我的问题是在“Property Changed”之前调用了“Output”。有没有办法使Object.Observe同步,或者我应该这样做更好吗? (我正在使用AngularJS,顺便说一句。)

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

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

3 个答案:

答案 0 :(得分:2)

Object.observe,&#34;遗憾地&#34; (请阅读下一篇),不执行同步任务。它会在&#34;微任务&#34;之后立即发送更改通知。结束。

这解释为here

  

多年的网络平台经验告诉我们,同步方法是您尝试的第一件事,因为它最容易包围。问题是它创建了一个根本危险的处理模型。如果您正在编写代码并说,更新对象的属性,那么您真的不希望更新该对象属性的情况可能会邀请一些任意代码去做他们想做的任何事情。当你在一个函数中间运行时,让你的假设失效是不理想的。

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

拥有同步事件的经典方法是改为使用getter和setter:

Person.prototype.setName = function(name) {
    this.name = name;
    console.log("Name Changed");
};

p.setName("Bob");

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

答案 1 :(得分:1)

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将负责更新视图等。

E.g:

<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;
                        }
                    });
                });
            });
        };
    };
});

另请参阅此 short Angular demo


更好的是,请看 more "real-worldy" demo 基本上,使用O.o代替Angular的脏检查的优势在于您可以节省$$watchers,因此$digest周期更快,更便宜。
当ES6出来时,Angular也会使用这种机制(O.o)。

答案 2 :(得分:1)

正如你所说,观察不是同步的。但你可以让手表进行回调并在那里更新“someOtherProperty”。喜欢这个

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

Updated jsfiddle