ko可观察对象或功能

时间:2016-01-28 15:40:00

标签: javascript knockout.js

我试图理解将observable保持为函数或将其设置为对象之间的区别

我的可观察项目:

self.SelectedItem = ko.observable();

我的观点:

  <div class="collapse" data-bind="with: SelectedItem">
    @Html.Action("Profile", "Team")
  </div>
  <div class="collapse" data-bind="with: ItemForEditing">
    @Html.Action("EditProfile", "Team")
  </div>

从网格中选择项目:

ko.utils.extend(TeamManager.prototype, {
  selectItem: function (item) {   
    if (typeof item == "undefined") return;  
    this.SelectedItem(item);  
    //this.SelectedItem = item;  
    ko.mapping.fromJS(ko.mapping.toJS(item), this.ItemForEditing);   
  },

  acceptItem: function (itemData) {
    ko.mapping.fromJS(itemData, {}, this.SelectedItem);
  },

为什么我对视图的绑定仅在可观察SelectedItem作为函数调用时才起作用,例如this.SelectedItem(item);,而不是作为赋值,例如this.SelectedItem = item;。后者导致部分视图无法呈现。

如果我像this.SelectedItem(item);这样使用它一切都好 - 为什么我需要这样使用它?

2 个答案:

答案 0 :(得分:3)

那只是how observables work。 knockout observable将您的属性/变量转换为函数。要从中获取值,您必须在没有参数的情况下调用该函数。要设置该值,请使用新值调用它。使用新值调用函数的行为允许knockout更新任何订阅,包括绑定。

当你这样做时:

this.SelectedItem = item; 

SelectedItem已不再是observable,已更新为直接指向item,因此不会更新任何绑定。

话虽如此,如果你在现代环境中工作,淘汰的主要作者之一已经a plugin允许更常见的属性访问,其中一项任务可以工作。

答案 1 :(得分:2)

在knockout.js中,所有可观察对象都形成为包含数据的函数。由于没有真正容易等待跟踪=对象的更改,因此knockoutjs的开发人员决定不使用对象的getter / setter方法,而是使用函数是实现跟踪更改的最简单方法。 / p>

对象上的Getter / Setter是旧版浏览器不具备的新功能,但可以调用JavaScript函数,并且可以访问调用此方法的方法arguments.callee

https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Fonctions/arguments/callee

为了能够实现跟踪,无论何时调用observable方法,它都将使用被调用者构建依赖树,这样,KnockoutJS能够知道在调用observable时要更新的内容。

假设你有这个对象:

t.fun = ko.Observable(10)

每当您使用该方法时。 t.fun()您实际上正在调用该方法,但是当fun的值发生变化时,淘汰会将此地点注册为需要被称为字母的地方。

当您致电t.fun(value)时,它会触发更改并再次调用已调用的地方。

这导致了为什么在淘汰赛中,每个观察者必须被召唤至少一次,才能抓住任何变化。如果您不以任何方式致电t.fun(),则knockoutjs不知道您的代码的这一部分取决于t.fun

此外,只要您将t.fun影响到其他任何事情......

t.foo = t.fun

t.foo应该与t.fun完全相同。

因此,如果在对象中设置了值。与其他语言不同,它始终是一种价值,t.fun = 1不会更新t.fun,而是将t.fun的值替换为1。 JavaScript可能会对基本类型进行更新,但在对象的情况下,您正在影响一个普通的新值,并且JavaScript无法知道该值是否已更改。

另一方面,

AngularJS通过将旧值与新值进行比较来检查值的变化。 KnockoutJS只是跟踪值的变化,因此不需要进行任何比较。

这是一个示例小提琴https://jsfiddle.net/gatnfbao/1/

var ViewModel = function(first, last) {
    this.firstName = first;
    this.lastName = ko.observable(last);

    this.fullName = ko.computed(function() {
        // Knockout tracks dependencies automatically. 
        // It knows that fullName depends on firstName and lastName,
        // because these get called when evaluating fullName.
        return this.firstName + " " + this.lastName();
    }, this);
};

ko.applyBindings(new ViewModel("Planet", "Earth")); // This makes

您可以看到计算字段正在使用this.firstNamethis.lastName(),它会告诉已淘汰computed字段是基于lastName。当knockoutjs启动时,它将尝试调用每个ko.computed变量以将它们添加到依赖关系树中。您可以在我们的viewModel中看到,正确计算了字段fullname:Planet Earth!。但是,如果您修改输入文本中的字段,它将不会更改ko.computed中的任何内容。但是,如果您更改“姓氏”字段,它会考虑两个字段来更新fullName,因为它们都存在。如果您修改firstName,则只会在lastName更改后才会看到结果。

但现在,我们将更改一些代码,以便您了解当您修改lastName时,您会发现计算值使用firstName的正确值作为{{的副作用1}}是一个可观察但lastName不是......让我们删除所有可观察者! https://jsfiddle.net/gatnfbao/2/

firstName

请注意,现在计算字段使用var ViewModel = function(first, last) { this.firstName = first; this.lastName = last; this.fullName = ko.computed(function() { return this.firstName + " " + this.lastName; }, this); }; ko.applyBindings(new ViewModel("Planet", "Earth")); // This makes 而不是this.lastName。现在,淘汰赛对this.lastName()值依赖的内容一无所知。

computed将使用值ko.computedPlanet调用一次,但由于它没有使用可观察对象,Earth不知道发生了什么那里。因此,如果您修改字段ko.computedfirstName,则永远不会更新lastName。它是用初始值计算的,但它不包含任何可观察的值。