使用计算属性下(Computed Observable)

依赖跟踪是如何工作的

初学者可以不必知道这一点,但是高级开发人员可以通过这节来了解依赖监控属性可以通过KO自动跟踪并被更新到UI上。

事实上它是很简单的,甚至简单的有点可爱,跟踪算法是这样的:

  • 1、 当你声明一个依赖属性时,KO会立即调用求值算法得到其初始值;
  • 2、 当你的计算函数运行的时候,KO会把监控属性通过计算得到的值都记录在一个Log中;
  • 3、 当你的计算结束的时候,KO会订阅能够访问的监控属性或依赖属性,订阅的回调函数是重新运行你的计算函数,循环整个过程,回到步骤1(并且注销不再使用的订阅);
  • 4、 KO会通知所有的订阅者,你的依赖属性已经被设置了新值。

所以说,KO并不仅仅是在第一次执行计算函数时检测你的依赖项,它每次都会检测。这意味着,你的依赖是可以动态的,举例来说:依赖A能决定你是否也依赖于B或C,这时候只有当A或者你选择的B或者C发生变化时计算函数才能运行。你不需要定义依赖关系:在代码运行时会自动检测到。

另外声明绑定是依赖属性的一种简单优美的实现。所以,一个绑定是读取监控属性的值,这个绑定变成这个监控属性的依赖,当监控属性发生改变的时候,会引起这个绑定被重新计算。

使用peek控制依赖

Knockout的自动依赖跟踪通常不是你想要的,但是你有时可能需要控制那些会更新依赖属性值的监控属性,特别是依赖属性会执行某些操作时,比如一个Ajax请求。peek方法可以帮助你在不需要创建依赖的情况下去控制一个监控属性或者依赖属性。

在下面的例子中,依赖属性通过Ajax方法和其他两个监控属性参数来重新加载一个名为currentPageData的监控属性。当pageIndex发生变化时,依赖属性会被更新,但会忽略掉selectedItem的变化,因为它是通过peek方法控制的。在这种情况下,用户可能希望仅仅在数据被加载时才使用selectedItem的当前值用于追踪。

    ko.computed(function () {
        varparams = {
            page: this.pageIndex(),
            selected: this.selectedItem.peek()
        };
        $.getJSON('/Some/Json/Service', params, this.currentPageData);
    }, this);

注意:如果你不想一个依赖属性过于频繁的更新,你可以参考throttle扩展。

注意:为什么循环依赖是没有意义的

依赖属性是一个虚设的监控属性输入到一个单一的监控属性输出之间的映射。因此,它并不会包含在你的依赖链循环中。循环不类似于递归,它们类似于各自含有计算方法的两个电子表格的单元格。这将导致一个无限的运算循环。

如果你的依赖图当中含有一个循环的话,Knockout是如何处理的呢?可以通过执行下面的规则来避免无限循环:Knockout当它已经运算过它就不会再重新运算。这个不太可能影响你的代码。在下面两种有关情况下:当两个依赖属性互相依赖(可能其中一个或两个都使用了deferEvaluation选项),或者一个依赖属性写到另外一个含有依赖关系的依赖属性上(无论是直接或间接的通过依赖链)。如果你想使用这些模式并且想完全避免循环依赖,你可以使用peek方法来实现上述功能。

确定一个属性是依赖属性

在某些情况下,通过编程的方式来处理一个依赖属性是非常有用的方法。Knockout 提供了一个很实用的方法:ko.isComputed。例如,你可能想要从发给服务器的数据中排除依赖属性。

    for(var prop in myObject){
        if (myObject.hasOwnProperty(prop) && !ko.isComputed(myObject[prop]))
        {
            result[prop] = myObject[prop];
        }
    }

此外,Knockout提供了类似的方法用来操作监控属性或者依赖属性:

ko.isObservable-当是observables、observableArrays或者 computed observables时返回true

ko.isWriteableObservable-当是observables、observableArrays或者可写的 computed observables时返回true

依赖属性参考

一个依赖属性可以通过以下方式实现:

1、ko.computed( evaluator [, targetObject, options] )这是用于创建依赖属性的一种最常见的方式。

evaluator--用于计算一个依赖属性当值的方法

targetObject--如果给定,当KO调用回调函数时,定义一个值表示

this--参阅管理”this”来了解更多信息。

options--依赖属性的参数对象。可以参看下面详细的清单。

2、ko.computed( options )--单一参数方式接受一个JavaScript对象或者以下任意属性来创建一个依赖属性

read--必需参数,传入方法。用于运算当前依赖属性当前值的方法。

write–可选参数,传入方法。如果给定,将会使依赖属性可写这个方法接收一个外部的值来写入到依赖属性中。它通常是使用你自己定义的逻辑来处理传入的值,通常将值写入到相关的监控属性中。

owner–可选参数,传入对象。

传入的对象作为this的关键字在KO调用read和write方法使用。

deferEvaluation–可选参数,传入ture或者false。如果设置为true,则依赖属性的值直到有实际访问它之前它的值是不会重新计算的。默认情况下,依赖属性的值在创建过程中就已经初始化了。

disposeWhen–可选参数,传入方法。如果给出,该传入方法将会在每一次运算结束之后被调用来释放依赖属性。真正的结果就是触发依赖属性的disposal方法。

disposeWhenNodeIsRemoved–可选参数,传入方法。如果给出,当指定的DOM元素被KO删除的时候依赖属性的disposal方法会被触发。当元素的绑定被模版或者控制流程绑定方法移除的时候,此功能是用来释放依赖属性。

依赖属性提供了以下方法:

dispose()–释放依赖属性,清除所有的依赖订阅。此方法非常有用,当你想停止一个依赖属性以避免其更新或者清除一个内存中的依赖属性而那些存在依赖关系的监控值是不会被清除的。

extend(extenders)–用于扩展依赖属性。

getDependenciesCount()–返回依赖属性当前依赖关系数量。

getSubscriptionsCount()–返回依赖属性当前订阅数量(无论是其他的依赖属性或手动订阅)。

isActive ()–返回依赖属性在以后是否会被更新,一个依赖属性如果没有依赖关系是无效的。

peek ()–返回当前依赖属性的值而无需创建依赖关系(可以参考peek)。

subscribe( callback [,callbackTarget, event] )–注册一个手动订阅来通知依赖属性的变化。

依赖属性发生了什么

在Knockout2.0之前,计算属性被称之为依赖属性,在2.0版本中,我们决定重命名ko.dependentObservable为ko.computed,因为它在读、解释和类型上更简单。但你不用担心:这不会破坏当前所有的代码。在实际使用中,ko.dependentObservable和ko.computed是等价的。