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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。