4

我一直在 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链接,带有 jQ​​uery。

我的问题是在“属性更改”之前调用了“输出”。有什么方法可以使 Object.Observe 同步,还是我应该这样做更好?(我正在使用 AngularJS,顺便说一句。)

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

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

4

3 回答 3

2

Object.observe, "sadly" (阅读下一篇), 不执行同步任务。一旦“微任务”结束,它就会发送更改通知。

这是解释here

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

因此,您的“微任务”在被调用后结束, 然后通知对象的更改。console.log("Output") Object.observe

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

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

p.setName("Bob");

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

于 2014-07-31T09:44:38.353 回答
1

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

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

更新了 jsfiddle

于 2014-07-31T09:53:30.890 回答
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 中,就会负责更新视图等。

例如:

<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

于 2014-07-31T09:51:13.803 回答