Scope對象其實就是一個簡單的POJO(plain old JavaScript Object)。咱們能夠給它任意的添加屬性。javascript
// scope.js export default class Scope { } // test.js const scope = new Scope(); scope.aProperty = 1; expect(scope.aProperty).toBe(1);
$watch
和 $digest
$watch
和$digest
就像一個硬幣的兩面。他們組合在一塊兒就是髒檢查循環的核心:對於數據變化的響應。java
$watch(watchExpression, listener, [objectEquality]);
angularjs中將全部的 watchExpression
存放到一個叫做$$watcher
的數組中,所以咱們建立一個數組:angularjs
$$watchers = []; $watch(watchFn, listenerFn) { const watcher = { watchFn, listenerFn }; this.$$watchers.push(watcher); }
它遍歷scope上的全部watchers,計算 watchExpression ,而且調用它對應的 listenerFn。數組
_.forEach(this.$$watchers, watcher => { watcher.listenerFn(); });
目的:只有監控的值發生改變的時候咱們才執行對應的listener。
思路:存儲上一次的值,和這一次值的進行比對。函數
$digest() { // 將變量聲明提取到循環外面 let newValue; let oldValue; _.forEach(this.$$watchers, watcher => { newValue = watcher.watchFn(this); // 第一次獲取last的時候值爲undefined oldValue = watcher.last; // 只有當新舊值不相等的時候才執行listener if (newValue !== oldValue) { watcher.last = newValue; watcher.listenerFn(newValue, oldValue, this); } }); }
angularjs中的初始化值爲一個函數:ui
function initWatchVal() {}
而後將其賦值給watch的last:this
const watcher = { watchFn, listenerFn, last: initWacthVal };
添加一個幫助方法,將全部的watchFn運行一次,返回一個boolean值,表示是否有更新:code
$digest() { let newValue; let oldValue; // 標記是否爲髒 let dirty; // 上來先執行一次看是否全部值發生變化,若是有變化,則第二次執行watch do { // 初次進來設置爲false dirty = false; _.forEach(this.$$watchers, watcher => { newValue = watcher.watchFn(this); // 第一次獲取last的時候值爲undefined oldValue = watcher.last; // 只有當新舊值不相等的時候才執行listener if (newValue !== oldValue) { dirty = true; watcher.last = newValue; // watcher.listenerFn(newValue, oldValue, this); const temp = oldValue === initWacthVal ? newValue : oldValue; watcher.listenerFn(newValue, temp, this); } }); } while (dirty); }
若是watch一直爲不穩定的值,咱們須要中止髒檢查。angularjs中默認的ttl爲10,對外暴露可修改。對象
// 若是爲髒,而且ttl達到0的時候 if (dirty && !(ttl--)) { throw '10 digest iterations reached'; }
添加lastDirtyWatch
去判斷,看源碼。ip
$watch(watchExpression, listener, [objectEquality]);
判斷是否相等要包含數組和對象,angularjs內置的方法爲:
angular.equals(o1, o2);
而後替換原來的新舊值的判斷:
// 判斷是否相等 function areEqual(newValue, oldValue, valueEq) { if (valueEq) { return _.isEqual(newValue, oldValue); } else { return newValue === oldValue; } }
angularjs中的銷燬是給$watch的時候返回一個方法,當你調用的時候,直接將它從數組中移除。
$watch(watchFn, listenerFn, valueEq) { const watcher = { watchFn, listenerFn, valueEq, last: initWacthVal }; this.$$watchers.push(watcher); // 新加入一個watcher的時候將lastDirtyWatch初始化 this.lastDirtyWatch = null; return () => { const index = _.find(this.$$watchers, watcher); if (index >= 0) { this.$$watchers.splice(index, 1); } }; }
對內簡單的分享,記錄下來。參照書名: build your own angularjs