scope, $rootScope,$RootScopeProvider - acelan86/angular GitHub Wiki

$RootScopeProvider,$rootScope的提供者

function $RootScopeProvider() {
   ...
   this.$get = ['$injector', '$exceptionHandler', '$parse',
      function( $injector,   $exceptionHandler,   $parse) {
        function Scope() { ... }  //Scope声明
        ...
        $rootScope = new Scope(); //实例
        ...
        return $rootScope;
      }
   ]
}

Scope对象声明

angular中的Scope对象使用function方式定义

从全局来看这里的Scope对象声明其实只是为了实例$rootScope服务的,后面生成的子作用域都是通过$rootScope.$new()产生,形成以$rootScope为根的Scope链, 对全局暴露的是$RootScopeProvider, 它拥有$get方法,获取$rootScope

    function Scope() {

      this.$id = nextUid(); //指定uid
   
      //从这里看scope就是一个双向链表结构
      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.$$listeners = {};  
      this.$$isolateBindings = {};
    }

Scope的实例方法

Scope.prototype = {
      //创建子作用域
      $new: function(isolate) { ... },

      //检测数据变化回调函数注册方法
      $watch: function(watchExp, listener, objectEquality) { ... },
     
      //遍历watchers执行脏数据回调
      $digest: function() { ... },

      //作用域销毁方法
      $destroy: function() {
        ...
      },

      //执行表达式方法
      $eval: function(expr, locals) { ... },

      //异步表达式处理方法,其实就是放入异步回调数组。。
      $evalAsync: function(expr) { ... },

      //从外部调用angular表达式时使用的方法,比如DOM事件,XHR,setTimeout,或者第三方插件处理的表达式
      $apply: function(expr) { ... },

      //为scope添加监听事件
      $on: function(name, listener) { ... },

      //触发scope上的事件
      $emit: function(name, args) { ... },

      //广播一个事件
      $broadcast: function(name, args) { ... }
    };

Scope.prototype.$new方法

      //这货在这的用法感觉像是一个child的工厂方法
      Scope.prototype.$new = function(isolate) {
        var Child,
            child;

        ...
        if (isolate) {
          //隔离作用域,直接由Scope对象实例,不形成链
          child = new Scope();
          child.$root = this.$root;
        } else {
          //非隔离作用域,这里真简单暴力
          Child = function() {};
          Child.prototype = this; //继承
          child = new Child();
          child.$id = nextUid();
        }
        child['this'] = child;
        child.$$listeners = {};
        child.$parent = this;
        child.$$asyncQueue = [];
        child.$$watchers = child.$$nextSibling = child.$$childHead = child.$$childTail = null;
        child.$$prevSibling = this.$$childTail;

        //Scope链的连接,就是链表操作
        if (this.$$childHead) {
          this.$$childTail.$$nextSibling = child;
          this.$$childTail = child;
        } else {
          this.$$childHead = this.$$childTail = child;
        }
        return child;
      }

Scope.prototype.$digest方法

  
      Scope.prototype.$digest = function() {
        var watch, value, last,
            watchers,
            asyncQueue,
            length,
            dirty, ttl = TTL,
            next, current, target = this,
            watchLog = [],
            logIdx, logMsg;

        beginPhase('$digest'); //rootScope.$$phase = '$digest';声明处于digest阶段,注意这里是从rootScope上声明

        do {
          dirty = false;
          current = target;
          do {
            asyncQueue = current.$$asyncQueue;
            //先处理当前scope实例上的异步队列中的表达式
            while(asyncQueue.length) {
              try {
                current.$eval(asyncQueue.shift());
              } catch (e) {
                $exceptionHandler(e);
              }
            }
            
            //执行$$watchers中的监测数据回调函数,没啥复杂,你懂的,找到当前监测数据值与上次值equal对比,dirty就执行回调方法,循环完所有的watcher就ok了
            if ((watchers = current.$$watchers)) {
              length = watchers.length;
              while (length--) {
                try {
                  //每一个watcher知道我关注的是那个model name
                  //通过watch.get(currentscope)获取当前scope中这个mode name的值,watch.last中存放上次值
                  //watch.fn中保存dirty的处理方法
                  watch = watchers[length];
                  if ((value = watch.get(current)) !== (last = watch.last) &&
                      !(watch.eq
                          ? equals(value, last)
                          : (typeof value == 'number' && typeof last == 'number'
                             && isNaN(value) && isNaN(last)))) {
                    dirty = true;
                    watch.last = watch.eq ? copy(value) : value;
                    watch.fn(value, ((last === initWatchVal) ? value : last), current);

                    //这段没看明白干啥?ttl(time to live?)
                    if (ttl < 5) {
                      logIdx = 4 - ttl;
                      if (!watchLog[logIdx]) watchLog[logIdx] = [];
                      logMsg = (isFunction(watch.exp))
                          ? 'fn: ' + (watch.exp.name || watch.exp.toString())
                          : watch.exp;
                      logMsg += '; newVal: ' + toJson(value) + '; oldVal: ' + toJson(last);
                      watchLog[logIdx].push(logMsg);
                    }
                  }
                } catch (e) {
                  $exceptionHandler(e);
                }
              }
            }

            // 深度优先遍历scope链,确保走完所有由当前节点出发的分支链
            if (!(next = (current.$$childHead || (current !== target && current.$$nextSibling)))) {
              while(current !== target && !(next = current.$$nextSibling)) {
                current = current.$parent;
              }
            }
          } while ((current = next));

          if(dirty && !(ttl--)) {
            clearPhase();
            throw Error(TTL + ' $digest() iterations reached. Aborting!\n' +
                'Watchers fired in the last 5 iterations: ' + toJson(watchLog));
          }
        } while (dirty || asyncQueue.length);

        //rootScope.$$phase = null; 退出处理阶段
        clearPhase();
      }

Scope.prototype.$watch

      Scope.prototype.$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 (!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);

        //这里居然返回function。。。逆天看不懂。。等后面看
        return function() {
          arrayRemove(array, watcher);
        };
      }