anjularjs中的双向绑定很有意思,下面模仿其源码,写了一个简单的版本,就是鲁棒性差了点。

    从"wo zi ji de"中可以看到,当输入框的内容变化时,其上2排内容也在动态变化。有点小兴奋啊。其中x变量用angularjs的双向绑定,y变量用的自己的(zi ji de)。

  HTML:

angularjs

wo zi ji de

    实现原理:

    1.$watch函数的2个参数,第一个是要监听的变量,第二个是变量改变时运行的函数。这2个存入JSON格式的watcher中,watchFn:该函数返回当前时刻的值,可能变了,可能没变。如下所示,last代表上次的值,初始为1.

var watcher = {					watchFn:function(watchValue,self){										//return self.data.get(watchValue);										return self.result2[0].value;									},					listenerFn:listenerFunction,					last:1				};

  2.由于不止监听一个变量,所以会有很多个watcher,所有watcher存入$$watcher中。两个美元代表私有变量,anjularjs中是代表$scope的私有变量,不让用户接触的。

this.$$watchers.push(watcher);

  3.有了要监听的变量,下一步就是实现监听了。就是$digest对象的职责了。$digest对$$watcher中的每一个watcher进行排查,看其当前值与last值是否不同,不同当然就说明被监听的值已经变了。那么对应使用ng-bind(by-bind)显示的值,也应该更新。这就是watcher中的listenerFn函数要干的事了。

 4.说明:ByScope对象来模仿$scope对象。$scope.x 对应html中<p ng-bind='x'/>和<input ng-model='x'/>。而ByScope模仿时(<p by-bind='y'/><input by-model='y'/>),是使用了一个map键值数组,'y'是key,key对应的值就相当于$scope.x;

       $scope.x 只需一句话,不用自己初始$scope变量,这些工作我都没做-_-!。你没听错,所以要自己建立ByScope对象,并将y值传入。  为什么呢?

   var bysowhat = new ByScope("y");

    anjularjs源码中,使用下面代码中的  get = compileToFn(watchExp, 'watch')。compileToFn可以把按照字符串的值转为对应的变量和函数,只有脚本语言可以办到哦。这个函数的源码我是编不完的。

$watch: function(watchExp, listener, objectEquality) {        var scope = this,            get = compileToFn(watchExp, 'watch'),            array = scope.$$watchers,            watcher = {              fn: listener,              last: initWatchVal,              get: get,              exp: watchExp,              eq: !!objectEquality            };

  

    JAVASCRIPT:

function ListControler($scope){			$scope.x = 100;				}					(function($){																	//深度优先遍历DOM树,找by-bin='y'的节点			//node:节点			//attri:"by-bind"			//返回值:目标结点数组			function bianli2(node,attri,value,resultNode){				var allNodes = node.childNodes;				if(allNodes != undefined){					//有子节点,继续递归					for(var i=0;i

    我的代码看完了,再来看看源码逐行解析

1.

 get = compileToFn(watchExp, 'watch'),

由于watchExp是通过字符串传递的,所以要把字符串转为变量或者函数(函数也是变量)。就像setInterval('myfunction',100)中的函数名字符串'myfunction'。

2.

 array = scope.$$watchers,            watcher = {              fn: listener,              last: initWatchVal,              get: get,              exp: watchExp,              eq: !!objectEquality            };

watcher对象存入监听过程中的基本信息: exp:要监听的变量;fn:发现变量数值变化时所执行的函数;last:变量上一次的值;eq:深度监听,get:如前所述。

3.

  // in the case user pass string, we need to compile it, do we really need this ?        if (!isFunction(listener)) {          var listenFn = compileToFn(listener || noop, 'listener');          watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};        }

如果通过$scope.$watch传递的第二个变量不是函数,是字符串,那么就把字符串转为函数,话说谁会传字符串啊_-! 你会放弃火车票,而拿火车票的相片给检票员看吗?这也是为什么源码注释成"do we really need this?"

4.

 if (typeof watchExp == 'string' && get.constant) {          var originalFn = watcher.fn;          watcher.fn = function(newVal, oldVal, scope) {            originalFn.call(this, newVal, oldVal, scope);            arrayRemove(array, watcher);          };        }

    为啥呢

5.

 if (!array) {          array = scope.$$watchers = [];        }        // we use unshift since we use a while loop in $digest for speed.        // the while loop reads in reverse order.        array.unshift(watcher);

    源码中,第一个watcher加入时,还没有array存在,所以这时要创建array。

            unshift代表在数组头插入数据,相当于把数组当栈用。为了后面$digest的循环速度。

6.

 return function() {          arrayRemove(array, watcher);        };

这构成了闭包,目的是让下面这行

arrayRemove(array, watcher);

在想被执行的时候再执行。

       */      $watch: function(watchExp, listener, objectEquality) {        var scope = this,            get = compileToFn(watchExp, 'watch'),            array = scope.$$watchers,            watcher = {              fn: listener,              last: initWatchVal,              get: get,              exp: watchExp,              eq: !!objectEquality            };        // in the case user pass string, we need to compile it, do we really need this ?        if (!isFunction(listener)) {          var listenFn = compileToFn(listener || noop, 'listener');          watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};        }        if (typeof watchExp == 'string' && get.constant) {          var originalFn = watcher.fn;          watcher.fn = function(newVal, oldVal, scope) {            originalFn.call(this, newVal, oldVal, scope);            arrayRemove(array, watcher);          };        }        if (!array) {          array = scope.$$watchers = [];        }        // we use unshift since we use a while loop in $digest for speed.        // the while loop reads in reverse order.        array.unshift(watcher);        return function() {          arrayRemove(array, watcher);        };      },