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

就像相等检测一样,Angular也内置了自己的深拷贝函数,但我们还是用Lo-Dash提供的。我们修改一下$digestOnce,在内部使用新的$$areEqual函数,如果需要的话,也复制最后一次的引用:

Scope.prototype.$$digestOnce = function() {
 var self = this;
 var dirty;
 _.forEach(this.$$watchers, function(watch) {
  var newValue = watch.watchFn(self);
  var oldValue = watch.last;
  if (!self.$$areEqual(newValue, oldValue, watch.valueEq)) {
   watch.listenerFn(newValue, oldValue, self);
   dirty = true;
  }
  watch.last = (watch.valueEq ? _.cloneDeep(newValue) : newValue);
 });
 return dirty;
};

现在我们可以看到两种脏检测方式的差异:

http://jsbin.com/ARiWENO/3/embed?js,console

相比检查引用,检查值的方式显然是一个更为复杂的操作。遍历嵌套的数据结构很花时间,保持深拷贝的数据也占用不少内存。这就是Angular默认不使用基于值的脏检测的原因,用户需要显式设置这个标记去打开它。

Angular也提供了第三种脏检测的方法:集合监控。就像基于值的检测,也能提示对象和数组中的变更。但不同于基于值的检测方式,它做的是一个比较浅的检测,并不递归进入到深层去,所以它比基于值的检测效率更高。集合检测是通过“$watchCollection”函数来使用的,在这个系列的后续部分,我们会来看看它是如何实现的。

在我们完成值的比对之前,还有些JavaScript怪事要处理一下。

非数字(NaN)

在JavaScript里,NaN(Not-a-Number)并不等于自身,这个听起来有点怪,但确实就这样。如果我们在脏检测函数里不显式处理NaN,一个值为NaN的监听器会一直是脏的。

对于基于值的脏检测来说,这个事情已经被Lo-Dash的isEqual函数处理掉了。对于基于引用的脏检测来说,我们需要自己处理。来修改一下$$areEqual函数的代码:

Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) {
 if (valueEq) {
  return _.isEqual(newValue, oldValue);
 } else {
  return newValue === oldValue ||
   (typeof newValue === 'number' && typeof oldValue === 'number' &&
    isNaN(newValue) && isNaN(oldValue));
 }
};

现在有NaN的监听器也正常了:

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

基于值的检测实现好了,现在我们该把注意力集中到应用程序代码如何跟作用域打交道上了。

$eval - 在作用域的上下文上执行代码

在Angular中,有几种方式可以在作用域的上下文上执行代码,最简单的一种就是$eval。它使用一个函数作参数,所做的事情是立即执行这个传入的函数,并且把作用域自身当作参数传递给它,返回的是这个函数的返回值。$eval也可以有第二个参数,它所做的仅仅是把这个参数传递给这个函数。

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

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