AngularJS-源码阅读(八.一)

Scope作用域,应该说这是AngularJS的理念核心之一。这个理念包含:

  • 全局唯一的rooScope和每个controller一个scope

  • scope之间的联系(父子关系,同代先后关系,独立scope)

  • 消息传递和注册($broadcast,$emit,$on)

  • model监听($watch)

  • $digest循环注入事件并启发。

scope之间关系是通过$new(isolate)来建立的。childScope =fatherScope.$new(),他们公用$$asyncQueue(存储注册事件)和$$postDigestQueue(存储$digest执行完后的响应事件)数组。这也就造成了随着应用的复杂度增加,会出现相应迟缓的现象。这也是为什么AngularJS限定不能超过2000个绑定对象。

$watch主要是将model以及listener注入到Scope $$watchers数组中。 $digest则是将$watch 的model变化检测出来,并执行其listener。

下面依旧在源码中讲解。(由于代码有点多,分拆成两篇文章)

function $RootScopeProvider(){
  var TTL = 10;
  var $rootScopeMinErr = minErr('$rootScope');
  var lastDirtyWatch = null;

  this.digestTtl = function(value) {
    if (arguments.length) {
      TTL = value;
    }
    return TTL;
  };
    //$injector 存储了注入module函数和值 具体[参看](http://my.oschina.net/myprogworld/blog/210610)
    //$parse 以后讲解,converts Angular expression into a function
    
  this.$get = ['$injector', '$exceptionHandler', '$parse', '$browser',
      function( $injector,   $exceptionHandler,   $parse,   $browser) {

   
    function Scope() {
      this.$id = nextUid();//unique string
      this.$$phase = this.$parent = this.$$watchers =
                     this.$$nextSibling = this.$$prevSibling =
                     this.$$childHead = this.$$childTail = null;
      this['this'] = this.$root =  this;
      this.$$destroyed = false;
      this.$$asyncQueue = [];
      this.$$postDigestQueue = [];
      this.$$listeners = {};
      this.$$listenerCount = {};
      this.$$isolateBindings = {};
    }




    Scope.prototype = {
      constructor: Scope,
  
      $new: function(isolate) {
          
        var ChildScope,
            child;

        if (isolate) {
          child = new Scope();
          child.$root = this.$root;
          // ensure that there is just one async queue per $rootScope and its children
          child.$$asyncQueue = this.$$asyncQueue;
          child.$$postDigestQueue = this.$$postDigestQueue;
        } else {
          ChildScope = function() {}; // should be anonymous; This is so that when the minifier munges
            // the name it does not become random set of chars. This will then show up as class
            // name in the web inspector.
          ChildScope.prototype = this;
          child = new ChildScope();
          child.$id = nextUid();
        }
        child['this'] = child;
        child.$$listeners = {};
        child.$$listenerCount = {};
        child.$parent = this;
        child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null;
        child.$$prevSibling = this.$$childTail;
        if (this.$$childHead) {
          this.$$childTail.$$nextSibling = child;
          this.$$childTail = child;
        } else {
          this.$$childHead = this.$$childTail = child;
        }
        return child;
      },

    
      $watch: function(watchExp, listener, objectEquality) {
        var scope = this,
            get = compileToFn(watchExp, 'watch'),//使用$parse转化成function,'watch'参数仅是用来标明异常提醒
            array = scope.$$watchers,
            watcher = {
              fn: listener,
              last: initWatchVal,
              get: get,//是不是将 get看做
              //function(){return eval(watchExp)} 会更简单易懂些?
              exp: watchExp,
              eq: !!objectEquality//转换为boolean
            };

        lastDirtyWatch = null;

        // in the case user pass string, we need to compile it, do we really need this ?
        //这个是容错判定,listener可能是个字符串 fucntion正如上面官方所说的那样,要不要都可以
        if (!isFunction(listener)) {
          var listenFn = compileToFn(listener || noop, 'listener');
          watcher.fn = function(newVal, oldVal, scope) {listenFn(scope);};
        }
        //有一点纳闷的是,为什么在初始化的时候就不将它初始化成[],而是初始化成null
        if (!array) {
          array = scope.$$watchers = [];
        }
        //get.constant  whether the expression is made entirely of JavaScript constant literals. {boolean}
        //当listener$parse为constant literals字面量时,且watchExp为字符串时,将这个watch改成一次性watch
        if (typeof watchExp == 'string' && get.constant) {
          var originalFn = watcher.fn;
          watcher.fn = function(newVal, oldVal, scope) {
            //$watch('name',function(new,old,scope){});
            originalFn.call(this, newVal, oldVal, scope);
            arrayRemove(array, watcher);//删除array中和watcher恒等的元素
          };
        }
        
        
        // we use unshift since we use a while loop in $digest for speed.
        // the while loop reads in reverse order.
        array.unshift(watcher);
        //var a=$watch(...)
        //a();就可以取消掉监听
        return function() {
          arrayRemove(array, watcher);
          lastDirtyWatch = null;
        };
      },

     //用来检测,obj具体属性是否更改,obj一般为{}或数组,更改就执行listener
     
      $watchCollection: function(obj, listener) {
        var self = this;
        var oldValue;
        var newValue;
        var changeDetected = 0;
        var objGetter = $parse(obj);
        var internalArray = [];
        var internalObject = {};
        var oldLength = 0;

        function $watchCollectionWatch() {
          newValue = objGetter(self);
          var newLength, key;

          if (!isObject(newValue)) {
            if (oldValue !== newValue) {
              oldValue = newValue;
              changeDetected++;
            }
          } else if (isArrayLike(newValue)) {
            if (oldValue !== internalArray) {
              // we are transitioning from something which was not an array into array.
              oldValue = internalArray;
              oldLength = oldValue.length = 0;
              changeDetected++;
            }

            newLength = newValue.length;

            if (oldLength !== newLength) {
              // if lengths do not match we need to trigger change notification
              changeDetected++;
              oldValue.length = oldLength = newLength;
            }
            // copy the items to oldValue and look for changes.
            for (var i = 0; i < newLength; i++) {
              if (oldValue[i] !== newValue[i]) {
                changeDetected++;
                oldValue[i] = newValue[i];
              }
            }
          } else {
            if (oldValue !== internalObject) {
              // we are transitioning from something which was not an object into object.
              oldValue = internalObject = {};
              oldLength = 0;
              changeDetected++;
            }
            // copy the items to oldValue and look for changes.
            newLength = 0;
            for (key in newValue) {
              if (newValue.hasOwnProperty(key)) {
                newLength++;
                if (oldValue.hasOwnProperty(key)) {
                  if (oldValue[key] !== newValue[key]) {
                    changeDetected++;
                    oldValue[key] = newValue[key];
                  }
                } else {
                  oldLength++;
                  oldValue[key] = newValue[key];
                  changeDetected++;
                }
              }
            }
            if (oldLength > newLength) {
              // we used to have more keys, need to find them and destroy them.
              changeDetected++;
              for(key in oldValue) {
                if (oldValue.hasOwnProperty(key) && !newValue.hasOwnProperty(key)) {
                  oldLength--;
                  delete oldValue[key];
                }
              }
            }
          }
          return changeDetected;
        }

        function $watchCollectionAction() {
          listener(newValue, oldValue, self);
        }

        return this.$watch($watchCollectionWatch, $watchCollectionAction);
      },
      //---参看下一篇文章

你可能感兴趣的:(AngularJS-源码阅读(八.一))