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