博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
knockout.js的学习笔记2
阅读量:6593 次
发布时间:2019-06-24

本文共 9299 字,大约阅读时间需要 30 分钟。

本节换一种方式解读,把我消化过的东西反刍出来可能这样大家容易理解些,knockout.js大量使用闭包,非常难读。

我们从viewModel看起:

function MyViewModel() {                this.firstName = $.observable('Planet');                this.lastName = $.observable('Earth');                this.fullName = $.computed({                    getter: function () {                        return this.firstName() + " " + this.lastName();                    },                    setter: function (value) {                        var lastSpacePos = value.lastIndexOf(" ");                        if (lastSpacePos > 0) { // Ignore values with no space character                            this.firstName(value.substring(0, lastSpacePos)); // Update "firstName"                            this.lastName(value.substring(lastSpacePos + 1)); // Update "lastName"                        }                    },                    scope: this                });            }            var a = new MyViewModel();            a.fullName("xxx yyy")

这里包含两种observable,没有依赖的与有依赖的,有依赖的通过没有依赖的计算出来,因此叫做computed!

但不管怎么样,它们都是返回一个函数,我们通过如下代码就可以模拟它们了:

//注:这里会用到mass Framework的种子模块的API https://github.com/RubyLouvre/mass-Framework/blob/master/src/mass.js            //observable的传参必须是基本类型            var validValueType = $.oneObject("Null,NaN,Undefined,Boolean,Number,String");           $.observable = function(value){                var v = value;//将上一次的传参保存到v中,ret与它构成闭包                function ret(neo){                    if(arguments.length){ //setter                        if(!validValueType[$.type(neo)]){                            $.error("arguments must be primitive type!")                            return ret                        }                        if(v !== neo ){                            v = neo;                        }                        return ret;                    }else{                //getter                        return v;                    }                }                value = validValueType[$.type(value)] ? value : void 0;                ret(arguments[0]);//必须先执行一次                return ret            }            $.computed = function(obj, scope){//为一个惰性函数,会重写自身                //computed是由多个$.observable组成                var getter, setter                if(typeof obj == "function"){                    getter = obj                }else if(obj && typeof obj == "object"){                    getter = obj.getter;                    setter = obj.setter;                    scope  = obj.scope;                }                var v                var ret = function(neo){                    if(arguments.length ){                        if(typeof setter == "function"){//setter不一定存在的                            if(!validValueType[$.type(neo)]){                                $.error("arguments must be primitive type!")                                return ret                            }                            if(v !== neo ){                                setter.call(scope, neo);                                v = neo;                            }                        }                        return ret;                    }else{                        v = getter.call(scope);                        return v;                    }                }                ret(); //必须先执行一次                return ret;            }

因此当我们执行new MyViewModel(),就会依次执行$.observable, $.observable, $.computed, $.computed中的参数的getter, getter再调用两个observable。

问题来了,当我们调用computed时,总会通知其依赖(即firstName ,lastName)进行更新,但firstName 发生改变时没有手段通知fullName 进行更新。ko把这逻辑写在dependencyDetection模块中。我简化如下:

$.dependencyDetection = (function () {                var _frames = [];                return {                    begin: function (ret) {                        _frames.push(ret);                    },                    end: function () {                        _frames.pop();                    },                    collect: function (self) {                        if (_frames.length > 0) {                            if(!self.list)                                self.list = [];                            var fn = _frames[_frames.length - 1];                            if ( self.list.indexOf( fn ) >= 0)                                return;                            self.list.push(fn);                        }                    }                };            })();

我们把它加入到 $.computed 与 $.observable中,再添加一个发布更新函数valueWillMutate

var validValueType = $.oneObject("Null,NaN,Undefined,Boolean,Number,String")            $.dependencyDetection = (function () {                var _frames = [];                return {                    begin: function (ret) {                        _frames.push(ret);                    },                    end: function () {                        _frames.pop();                    },                    collect: function (self) {                        if (_frames.length > 0) {                            if(!self.list)                                self.list = [];                            var fn = _frames[_frames.length - 1];                            if ( self.list.indexOf( fn ) >= 0)                                return;                            self.list.push(fn);                        }                    }                };            })();            $.valueWillMutate = function(observable){                var list = observable.list                if($.type(list,"Array")){                    for(var i = 0, el; el = list[i++];){                        el();                    }                }            }            $.observable = function(value){                var v = value;//将上一次的传参保存到v中,ret与它构成闭包                function ret(neo){                    if(arguments.length){ //setter                        if(!validValueType[$.type(neo)]){                            $.error("arguments must be primitive type!")                            return ret                        }                        if(v !== neo ){                            v = neo;                            $.valueWillMutate(ret);//向依赖者发送通知                        }                        return ret;                    }else{                //getter                        $.dependencyDetection.collect(ret);//收集被依赖者                        return v;                    }                }                value = validValueType[$.type(value)] ? value : void 0;                ret(arguments[0]);//必须先执行一次                return ret            }            $.computed = function(obj, scope){//为一个惰性函数,会重写自身                //computed是由多个$.observable组成                var getter, setter                if(typeof obj == "function"){                    getter = obj                }else if(obj && typeof obj == "object"){                    getter = obj.getter;                    setter = obj.setter;                    scope  = obj.scope;                }                var v                var ret = function(neo){                    if(arguments.length ){                        if(typeof setter == "function"){//setter不一定存在的                            if(!validValueType[$.type(neo)]){                                $.error("arguments must be primitive type!")                                return ret                            }                            if(v !== neo ){                                setter.call(scope, neo);                                v = neo;                            }                        }                        return ret;                    }else{                        $.dependencyDetection.begin(ret);//让其依赖知道自己的存在                        v = getter.call(scope);                        $.dependencyDetection.end();                        return v;                    }                }                ret(); //必须先执行一次                return ret;            }            function MyViewModel() {                this.firstName = $.observable('Planet');                this.lastName = $.observable('Earth');                this.fullName = $.computed({                    getter: function () {                        return this.firstName() + " " + this.lastName();                    },                    setter: function (value) {                        var lastSpacePos = value.lastIndexOf(" ");                        if (lastSpacePos > 0) { // Ignore values with no space character                            this.firstName(value.substring(0, lastSpacePos)); // Update "firstName"                            this.lastName(value.substring(lastSpacePos + 1)); // Update "lastName"                        }                    },                    scope: this                });                this.card = $.computed(function(){                    return this.fullName() +" 屌丝"                },this)            }            var a = new MyViewModel();            //============测试代码============            $.log(a.firstName())//Planet            $.log(a.lastName())//Earth            $.log(a.fullName())//Planet Earth 通过上面两个计算出来            a.fullName("xxx yyy");//更新fullName会自动更新firstName与lastName            $.log(a.firstName())//xxx            $.log(a.lastName())//yyy            a.firstName("ooo");//更新firstName会自动更新fullName            $.log(a.fullName())//ooo yyy            $.log(a.card())//ooo yyy 屌丝

到这里viewModel中的每个域(firstName, lastName, fullName)只要存在依赖关系都能相互通知了。

转载地址:http://asdio.baihongyu.com/

你可能感兴趣的文章
ubuntu 下安装 matplotlib
查看>>
webservice的几个简单概念
查看>>
underscore 1.7.0 api
查看>>
spring Transaction Management --官方
查看>>
iOS开发-清理缓存功能的实现
查看>>
IS_ERR、PTR_ERR、ERR_PTR
查看>>
html5 canvas 奇怪的形状垂直渐变
查看>>
mac java环境
查看>>
lamp 一键安装
查看>>
SQL Server 2008 收缩日志(log)文件
查看>>
UICollectionView基础
查看>>
SSAS中CUBE行权限数据级权限控制
查看>>
android学习记录(三)百度地图错误---只有一个电话显示帧,没有地图内容。
查看>>
BZOJ2794 : [Poi2012]Cloakroom
查看>>
Git查看、删除、重命名远程分支和tag【转】
查看>>
浅谈IM软件业务知识——非对称加密,RSA算法,数字签名,公钥,私钥
查看>>
Oracle中REGEXP_SUBSTR及其它支持正则表达式的内置函数小结
查看>>
正确计算linux系统内存使用率
查看>>
关于MapReduce单词统计的例子:
查看>>
【php】利用php的构造函数与析构函数编写Mysql数据库查询类 (转)
查看>>