Scope object
Scope物件其實就是一個簡單的POJO(plain old JavaScript Object)。我們可以給它任意的新增屬性。
// scope.js
export default class Scope {
}
// test.js
const scope = new Scope();
scope.aProperty = 1;
expect(scope.aProperty).toBe(1);
$watch
和 $digest
$watch
和$digest
就像一個硬幣的兩面。他們組合在一起就是髒檢查迴圈的核心:對於資料變化的響應。
$watch:
$watch(watchExpression, listener, [objectEquality]);
- watchExpression: 監聽的資料
- listener:資料發生變化的時候呼叫
- objectEquality: 後面單獨說
angularjs中將所有的 watchExpression
存放到一個叫作$$watcher
的陣列中,因此我們建立一個陣列:
$$watchers = [];
$watch(watchFn, listenerFn) {
const watcher = {
watchFn,
listenerFn
};
this.$$watchers.push(watcher);
}
$digest:
它遍歷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);
}
});
}
初始化watch的值
angularjs中的初始化值為一個函式:
function initWatchVal() {}
然後將其賦值給watch的last:
const watcher = {
watchFn,
listenerFn,
last: initWacthVal
};
持續監測
新增一個幫助方法,將所有的watchFn執行一次,返回一個boolean值,表示是否有更新:
$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);
}
ttl
如果watch一直為不穩定的值,我們需要停止髒檢查。angularjs中預設的ttl為10,對外暴露可修改。
// 如果為髒,並且ttl達到0的時候
if (dirty && !(ttl--)) {
throw '10 digest iterations reached';
}
停止髒檢查
新增lastDirtyWatch
去判斷,看原始碼。
- 執行$digest的時候將 lastDirtyWatch = null
- 當前watcer 和 lastDirtyWatch 相同的時候設定為 null
- watch的listener裡面包含有watch的時候,重置為 null
基於值的髒檢查
$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