knockout.js的学习笔记2

本节换一种方式解读,把我消化过的东西反刍出来可能这样大家容易理解些,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

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

 
 
标签:  knockoutjs

你可能感兴趣的:(KnockoutJS)