AngularJS-源碼閱讀(八.一)

Scope做用域,應該說這是AngularJS的理念核心之一。這個理念包含:web

  • 全局惟一的rooScope和每一個controller一個scopeexpress

  • scope之間的聯繫(父子關係,同代前後關係,獨立scope)數組

  • 消息傳遞和註冊($broadcast,$emit,$on)dom

  • model監聽($watch)async

  • $digest循環注入事件並啓發。ide

scope之間關係是經過$new(isolate)來創建的。childScope =fatherScope.$new(),他們公用$$asyncQueue(存儲註冊事件)和$$postDigestQueue(存儲$digest執行完後的響應事件)數組。這也就形成了隨着應用的複雜度增長,會出現相應遲緩的現象。這也是爲何AngularJS限定不能超過2000個綁定對象。函數

$watch主要是將model以及listener注入到Scope $$watchers數組中。 $digest則是將$watch 的model變化檢測出來,並執行其listener。oop

下面依舊在源碼中講解。(因爲代碼有點多,分拆成兩篇文章)post

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);
      },
      //---參看下一篇文章
相關文章
相關標籤/搜索