if (current.$$childHead) {
next = current.$$childHead;
} else if (current !== target && current.$$nextSibling) {
next = current.$$nextSibling;
}
while (!next && current !== target && !(next = current.$$nextSibling)) {
current = current.$parent;
}
} while (current = next);
第三层循环scope的 watchers
length = watchers.length;
while (length--) {
try {
watch = watchers[length];
// ... 省略
} catch (e) {
clearPhase();
$exceptionHandler(e);
}
}
3. $evalAsync
3.1 源码分析
$evalAsync用于延迟执行,源码如下:
function(expr) { if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) { $browser.defer(function() { if ($rootScope.$$asyncQueue.length) { $rootScope.$digest(); } }); } this.$$asyncQueue.push({scope: this, expression: expr}); }
通过判断是否已经有 dirty check 在运行,或者已经有人触发过$evalAsync
if (!$rootScope.$$phase && !$rootScope.$$asyncQueue.length) $browser.defer 就是通过调用 setTimeout 来达到改变执行顺序 $browser.defer(function() { //... });
如果不是使用defer,那么
function (exp) { queue.push({scope: this, expression: exp}); this.$digest(); } scope.$evalAsync(fn1); scope.$evalAsync(fn2); // 这样的结果是 // $digest() > fn1 > $digest() > fn2 // 但是实际需要达到的效果:$digest() > fn1 > fn2
上节 $digest 中省略了了async 的内容,位于第一层循环中
while(asyncQueue.length) { try { asyncTask = asyncQueue.shift(); asyncTask.scope.$eval(asyncTask.expression); } catch (e) { clearPhase(); $exceptionHandler(e); } lastDirtyWatch = null; }
简单易懂,弹出asyncTask进行执行。
不过这边有个细节,为什么这么设置呢?原因如下,假如在某次循环中执行到watchX时新加入1个asyncTask,此时会设置 lastDirtyWatch=watchX,恰好该task执行会导致watchX后续的一个watch执行出新值,如果没有下面的代码,那么下个循环到 lastDirtyWatch (watchX)时便跳出循环,并且此时dirty==false。
lastDirtyWatch = null;
还有这边还有一个细节,为什么在第一层循环呢?因为具有继承关系的scope其 $$asyncQueue 是公用的,都是挂载在root上,故不需要在下一层的scope层中执行。
2. 继承性
scope具有继承性,如 $parentScope, $childScope 两个scope,当调用 $childScope.fn 时如果 $childScope 中没有 fn 这个方法,那么就是去 $parentScope上查找该方法。
这样一层层往上查找直到找到需要的属性。这个特性是利用 javascirpt 的原型继承的特点实现。
源码:
function(isolate) { var ChildScope, child; if (isolate) { child = new Scope(); child.$root = this.$root; // isolate 的 asyncQueue 及 postDigestQueue 也都是公用root的,其他独立 child.$$asyncQueue = this.$$asyncQueue; child.$$postDigestQueue = this.$$postDigestQueue; } else { if (!this.$$childScopeClass) { this.$$childScopeClass = function() { // 这里可以看出哪些属性是隔离独有的,如$$watchers, 这样就独立监听了, this.$$watchers = this.$$nextSibling = this.$$childHead = this.$$childTail = null; this.$$listeners = {}; this.$$listenerCount = {}; this.$id = nextUid(); this.$$childScopeClass = null; }; this.$$childScopeClass.prototype = this; } child = new this.$$childScopeClass(); } // 设置各种父子,兄弟关系,很乱! child['this'] = child; child.$parent = this; child.$$prevSibling = this.$$childTail; if (this.$$childHead) { this.$$childTail.$$nextSibling = child; this.$$childTail = child; } else { this.$$childHead = this.$$childTail = child; } return child; }
代码还算清楚,主要的细节是哪些属性需要独立,哪些需要基础下来。
最重要的代码:
this.$$childScopeClass.prototype = this;
就这样实现了继承。
3. 事件机制
3.1 $on