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