knockout源码分析之computed(依赖属性)

一、序列图

二、主要代码文件

1、dependentObservable.js:主要包含ko.computed相关方法的处理 2、dependencyDetection.js:主要包含依赖的监控上下文对象。

三、主要逻辑

1、首先为某个属性定义 一个computed对象,如下源码:

var vModel = function(){
        this.fName = ko.observable('fName'),
        this.lName= ko.observable('lName'),
        this.name= ko.computed(function () { //监控依赖对象
            return this.fName() + '-' + this.lName();
        },this);
    };

2、当代码在执行ko.computed方法,求值方法被作为参数传入,并赋值给options的read属性

3、创建一个state字面量对象,其中包含read、write属性,如下代码:

var state = {
        latestValue: undefined,
        isStale: true,
        isBeingEvaluated: false,
        suppressDisposalUntilDisposeWhenReturnsFalse: false,
        isDisposed: false,
        pure: false,
        isSleeping: false,
        readFunction: options["read"],
        evaluatorFunctionTarget: evaluatorFunctionTarget || options["owner"],
        disposeWhenNodeIsRemoved: options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,
        disposeWhen: options["disposeWhen"] || options.disposeWhen,
        domNodeDisposalCallback: null,
        dependencyTracking: {},
        dependenciesCount: 0,
        evaluationTimeoutInstance: null
    };

4、生成computedObservable对象(function),然后将state附加到_state属性上,则扩展为发布/订阅对象。

5、扩展computedFn所有方法和属性到computedObservable对象上

// Inherit from 'subscribable'
    if (!ko.utils.canSetPrototype) {
        // 'subscribable' won't be on the prototype chain unless we put it there directly
        ko.utils.extend(computedObservable, ko.subscribable['fn']);
    }
    ko.subscribable['fn'].init(computedObservable); //执行发布/订阅对象的init方法,用于初始化发布/订阅对象。

    // Inherit from 'computed'
    ko.utils.setPrototypeOfOrExtend(computedObservable, computedFn);

6、然后执行computedObservable的evaluateImmediate方法,此方法中最重的三点:

   6.1、在evaluateImmediate_CallReadWithDependencyDetection方法中,创建了依赖监控对象,并添加到依赖监控上下文中

var isInitial = state.pure ? undefined : !state.dependenciesCount,   // If we're evaluating when there are no previous dependencies, it must be the first time
            dependencyDetectionContext = {
                computedObservable: computedObservable,
                disposalCandidates: state.dependencyTracking,
                disposalCount: state.dependenciesCount
            };

        ko.dependencyDetection.begin({
            callbackTarget: dependencyDetectionContext,
            callback: computedBeginDependencyDetectionCallback,
            computed: computedObservable,
            isInitial: isInitial
        });

    6.2、然后调用evaluateImmediate_CallReadThenEndDependencyDetection方法,参数传递的state(在ko.computed方法中定义的)、dependencyDetectionContext(依赖监控对象)

    6.3、其中用到了try catch finall方式,确保ko.dependencyDetection.end方法的执行

try {
            var readFunction = state.readFunction;
            return state.evaluatorFunctionTarget ? readFunction.call(state.evaluatorFunctionTarget) : readFunction();
        } finally {
            ko.dependencyDetection.end();

            // For each subscription no longer being used, remove it from the active subscriptions list and dispose it
            if (dependencyDetectionContext.disposalCount && !state.isSleeping) {
                ko.utils.objectForEach(dependencyDetectionContext.disposalCandidates, computedDisposeDependencyCallback);
            }

            state.isStale = false;
        }

7、在执行ko.computed的readFunction方法时,其中就执行了ko.observable方法(执行的是read),这时就会去调用ko.dependencyDetection.registerDependency方法(参数为此函数对象)

function observable() {
        if (arguments.length > 0) {
            // Write

            // Ignore writes if the value hasn't changed
            if (observable.isDifferent(observable[observableLatestValue], arguments[0])) {
                observable.valueWillMutate();
                observable[observableLatestValue] = arguments[0];
                observable.valueHasMutated();
            }
            return this; // Permits chained assignments
        }
        else {
            debugger;
            // Read
            ko.dependencyDetection.registerDependency(observable); //执行依赖
            return observable[observableLatestValue];
        }
    }

8、在ko.dependencyDetection中的registerDependency方法内,首先会判断ko.observable是否为订阅对象,如果是则执行begin加入的callbak函数.

registerDependency: function (subscribable) { //注入到相关依赖属性
            if (currentFrame) {
                if (!ko.isSubscribable(subscribable))
                    throw new Error("Only subscribable things can act as dependencies");
                currentFrame.callback.call(currentFrame.callbackTarget, subscribable, subscribable._id || (subscribable._id = getId()));
            }
        }

9、执行evaluateImmediate方法后,然后注册Dom移除回调事件。

if (state.disposeWhenNodeIsRemoved && computedObservable.isActive()) {
        ko.utils.domNodeDisposal.addDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback = function () {
            computedObservable.dispose();
        });
    }

10、返回computedObservable对象

四、补充说明

1、ko.dependencyDetection中有ignore方法,他主要实现的是一个异步锁,让callbcak处于锁的状态执行

ignore: function (callback, callbackTarget, callbackArgs) { //按顺序s执行依赖,但不触发订阅。
            try {
                begin();
                return callback.apply(callbackTarget, callbackArgs || []);
            } finally {
                end();
            }
        }

2、ko.computed 与 ko.dependentObservable是相同的。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏积累沉淀

Python快速学习第十一天--Python多线程

Python中使用线程有三种方式: 方法一:函数式 调用thread模块中的start_new_thread()函数来产生新线程。语法如下: thread...

24190
来自专栏hbbliyong

C学习笔记(1)-结构体、预处理与多文件结构程序设计

一、结构体的定义与使用 #include <stdio.h> //定义结构体(类似模板) typedef struct { char name[50]; ...

30040
来自专栏mwangblog

bash中的变量

13180
来自专栏与神兽党一起成长

使用commons-pool管理FTP连接

在封装一个FTP工具类文章,已经完成一版对FTP连接的管理,设计了模板方法,为工具类上传和下载文件方法的提供获取对象和释放对象支持。

15120
来自专栏Java帮帮-微信公众号-技术文章全总结

stuts2返回json数据简单实现

stuts2返回json数据简单实现 整理了下struts2的简单demo,完成JSON数据的展示。基本流程是在Action定义一个对象属性,例如map类型的一...

39670
来自专栏技巅

GlusterFS之内存池(mem-pool)使用实例分析

20260
来自专栏C语言及其他语言

每日一题[C语言程序设计教程(第三版)课后习题1.5]

请参照本章例题,编写一个C程序,输出以下信息: ************************** Hello World! ****************...

27330
来自专栏Java架构师历程

JVM加载class文件的原理

当Java编译器编译好.class文件之后,我们需要使用JVM来运行这个class文件。那么最开始的工作就是要把字节码从磁盘输入到内存中,这个过程我们叫做【加载...

54520
来自专栏拂晓风起

C++调用C链接库会出现的问题

14630
来自专栏Janti

Java多线程高并发学习笔记(一)——Thread&Runnable

 进程与线程 首先来看百度百科关于进程的介绍: 进程是一个具有独立功能的程序关于某个数据集合的一次运行活动。它可以申请和拥有系统资源,是一个动态的概念,是一个活...

754100

扫码关注云+社区

领取腾讯云代金券