声明为对象文字的挖空视图模型与函数之间的差异

在Knockout JS中,我看到视图模型声明为:

var viewModel = {
firstname: ko.observable("Bob")
};


ko.applyBindings(viewModel );

或:

var viewModel = function() {
this.firstname= ko.observable("Bob");
};


ko.applyBindings(new viewModel ());

如果有的话,这两者之间有什么区别?

我确实在KnockoutJS谷歌组上找到了这次讨论,但它并没有真正给我一个满意的答案。

如果我想用一些数据初始化模型,我可以看到一个原因,例如:

var viewModel = function(person) {
this.firstname= ko.observable(person.firstname);
};


var person = ... ;
ko.applyBindings(new viewModel(person));

但如果我不这样做,我选择哪种风格有关系吗?

44270 次浏览

There are a couple of advantages to using a function to define your view model.

The main advantage is that you have immediate access to a value of this that equals the instance being created. This means that you can do:

var ViewModel = function(first, last) {
this.first = ko.observable(first);
this.last = ko.observable(last);
this.full = ko.computed(function() {
return this.first() + " " + this.last();
}, this);
};

So, your computed observable can be bound to the appropriate value of this, even if called from a different scope.

With an object literal, you would have to do:

var viewModel = {
first: ko.observable("Bob"),
last: ko.observable("Smith"),
};


viewModel.full = ko.computed(function() {
return this.first() + " " + this.last();
}, viewModel);

In that case, you could use viewModel directly in the computed observable, but it does get evaluated immediate (by default) so you could not define it within the object literal, as viewModel is not defined until after the object literal closed. Many people don't like that the creation of your view model is not encapsulated into one call.

Another pattern that you can use to ensure that this is always appropriate is to set a variable in the function equal to the appropriate value of this and use it instead. This would be like:

var ViewModel = function() {
var self = this;
this.items = ko.observableArray();
this.removeItem = function(item) {
self.items.remove(item);
}
};

Now, if you are in the scope of an individual item and call $root.removeItem, the value of this will actually be the data being bound at that level (which would be the item). By using self in this case, you can ensure that it is being removed from the overall view model.

Another option is using bind, which is supported by modern browsers and added by KO, if it is not supported. In that case, it would look like:

var ViewModel = function() {
this.items = ko.observableArray();
this.removeItem = function(item) {
this.items.remove(item);
}.bind(this);
};

There is much more that could be said on this topic and many patterns that you could explore (like module pattern and revealing module pattern), but basically using a function gives you more flexibility and control over how the object gets created and the ability to reference variables that are private to the instance.

I use a different method, though similar:

var viewModel = (function () {
var obj = {};
obj.myVariable = ko.observable();
obj.myComputed = ko.computed(function () { return "hello" + obj.myVariable() });


ko.applyBindings(obj);
return obj;
})();

Couple of reasons:

  1. Not using this, which can confusion when used within ko.computeds etc
  2. My viewModel is a singleton, I don't need to create multiple instances (i.e. new viewModel())