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); }, //---参看下一篇文章