進入正文前的說明:本文中的示例代碼并非AngularJs源碼,而是來自書籍<<Build Your Own AngularJs>>, 這本書的作者僅依賴jquery和lodash一步一步構建出AngularJs的各核心模塊,對全面理解AngularJs有非常巨大的幫助。若有正在使用AngulaJs攻城拔寨并且希望完全掌握手中武器的小伙伴,相信能對你理解AngularJs帶來莫大幫助,感謝作者。
在這篇文章中,希望能讓您理清楚以下幾項與scope相關的功能:
1.dirty-checking(臟檢測)核心機制,主要包括:$watch 和 $digest;
2.幾種不同的觸發$digest循環的方式:$eval, $apply, $evalAsync, $applyAsync;
3.scope的繼承機制以及isolated scope;
4.依賴于scope的事件循環:$on, $broadcast, $emit.
現在開始我們的第一部分:scope和dirty-checking
dirty-checking(臟檢測)原理簡述:scope通過$watch方法向this.$$watchers數組中添加watcher對象(包含watchFn, listenerFn, valueEq, last 四個屬性)。每當$digest循環被觸發時,它會遍歷$$watchers數組,執行watcher中的watchFn,獲取當前scope上某屬性的值(一個watcher對應scope上一個被監聽屬性),然后去同watcher中的last(上一次的值)做比較,若兩值不相等,就執行listenerFn。
function Scope() { this.$$watchers = []; // 監聽器數組 this.$$lastDirtyWatch = null; // 每次digest循環的最后一個臟的watcher, 用于優化digest循環 this.$$asyncQueue = []; // scope上的異步隊列 this.$$applyAsyncQueue = []; // scope上的異步apply隊列 this.$$applyAsyncId = null; //異步apply信息 this.$$postDigestQueue = []; // postDigest執行隊列 this.$$phase = null; // 儲存scope上正在做什么,值有:digest/apply/null this.$root = this; // rootScope this.$$listeners = {}; // 存儲包含自定義事件鍵值對的對象 this.$$children = []; // 存儲當前scope的兒子Scope,以便$digest循環遞歸}實際上scope就是一個普通的javascript對象,一個類構造函數,可以通過new進行實例化。根據臟檢測的原理,接下來,我們一起看看scope的$watch方法的實現。
/* $watch方法:向watchers數組中添加watcher對象,以便對應調用 */Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) { var self = this; watchFn = $parse(watchFn); // watchDelegate: 針對watch expression是常量和 one-time-binding的情況,進行優化。在第一次初始化之后刪除watch if(watchFn.$$watchDelegate) { return watchFn.$$watchDelegate(self, listenerFn, valueEq, watchFn); } var watcher = { watchFn: watchFn, listenerFn: listenerFn || function() {}, valueEq: !!valueEq, last: initWatchVal }; this.$$watchers.unshift(watcher); this.$root.$$lastDirtyWatch = null; return function() { var index = self.$$watchers.indexOf(watcher); if(index >= 0) { self.$$watchers.splice(index, 1); self.$root.$$lastDirtyWatch = null; } };};
新聞熱點
疑難解答
圖片精選