深入理解Angularjs 脏值检测(5)

事实上,Angular里面的TTL是可以调整的。我们将在后续文章讨论provider和依赖注入的时候再回顾这个话题。

我们继续,给外层digest循环添加一个循环计数器。如果达到了TTL,就抛出异常:

Scope.prototype.$digest = function() {
 var ttl = 10;
 var dirty;
 do {
  dirty = this.$$digestOnce();
  if (dirty && !(ttl--)) {
   throw "10 digest iterations reached";
  }
 } while (dirty);
};

下面是更新过的版本,可以让我们循环引用的监控例子抛出异常:

http://jsbin.com/uNapUWe/2/embed?js,console

这些应当已经把digest的事情说清楚了。

现在,我们把注意力转到如何检测变更上吧。

基于值的脏检查

我们曾经使用严格等于操作符(===)来比较新旧值,在绝大多数情况下,它是不错的,比如所有的基本类型(数字,字符串等等),也可以检测一个对象或者数组是否变成新的了,但Angular还有一种办法来检测变更,用于检测当对象或者数组内部产生变更的时候。那就是:可以监控值的变更,而不是引用。

这类脏检查需要给$watch函数传入第三个布尔类型的可选参数当标志来开启。当这个标志为真的时候,基于值的检查开启。我们来重新定义$watch,接受这个参数,并且把它存在监听器里:

Scope.prototype.$watch = function(watchFn, listenerFn, valueEq) {
 var watcher = {
  watchFn: watchFn,
  listenerFn: listenerFn,
  valueEq: !!valueEq
 };
 this.$$watchers.push(watcher);
};

我们所做的一切是把这个标志加在监听器上,通过两次取反,强制转换为布尔类型。当用户调用$watch,没传入第三个参数的时候,valueEq会是未定义的,在监听器对象里就变成了false。

基于值的脏检查意味着如果新旧值是对象或者数组,我们必须遍历其中包含的所有内容。如果它们之间有任何差异,监听器就脏了。如果该值包含嵌套的对象或者数组,它也会递归地按值比较。

Angular内置了自己的相等检测函数,但是我们会用Lo-Dash提供的那个。让我们定义一个新函数,取两个值和一个布尔标志,并比较相应的值:

Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) {
 if (valueEq) {
  return _.isEqual(newValue, oldValue);
 } else {
  return newValue === oldValue;
 }
};

为了提示值的变化,我们也需要改变之前在每个监听器上存储旧值的方式。只存储当前值的引用是不够的,因为在这个值内部发生的变更也会生效到它的引用上,$$areEqual方法比较同一个值的两个引用始终为真,监控不到变化,因此,我们需要建立当前值的深拷贝,并且把它们储存起来。

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/219.html