$eval的实现很简单:
Scope.prototype.$eval = function(expr, locals) { return expr(this, locals); };
$eval的使用一样很简单:
http://jsbin.com/UzaWUC/1/embed?js,console
那么,为什么要用这么一种明显很多余的方式去执行一个函数呢?有人觉得,有些代码是专门与作用域的内容打交道的,$eval让这一切更加明显。$scope也是构建$apply的一个部分,后面我们就来讲它。
然后,可能$eval最有意思的用法是当我们不传入函数,而是表达式。就像$watch一样,可以给$eval一个字符串表达式,它会把这个表达式编译,然后在作用域的上下文中执行。我们将在这个系列的后面部分实现这些。
$apply - 集成外部代码与digest循环
可能Scope上所有函数里最有名的就是$apply了。它被誉为将外部库集成到Angular的最标准的方式,这话有个不错的理由。
$apply使用函数作参数,它用$eval执行这个函数,然后通过$digest触发digest循环。下面是一个简单的实现:
Scope.prototype.$apply = function(expr) { try { return this.$eval(expr); } finally { this.$digest(); } };
$digest的调用放置于finally块中,以确保即使函数抛出异常,也会执行digest。
关于$apply,大的想法是,我们可以执行一些与Angular无关的代码,这些代码也还是可以改变作用域上的东西,$apply可以保证作用域上的监听器可以检测这些变更。当人们谈论使用$apply集成代码到“Angular生命周期”的时候,他们指的就是这个事情,也没什么比这更重要的了。
这里是$apply的实践:
http://jsbin.com/UzaWUC/2/embed?js,console
延迟执行 - $evalAsync
在JavaScript中,经常会有把一段代码“延迟”执行的情况 - 把它的执行延迟到当前的执行上下文结束之后的未来某个时间点。最常见的方式就是调用setTimeout()函数,传递一个0(或者非常小)作为延迟参数。
这种模式也适用于Angular程序,但更推荐的方式是使用$timeout服务,并且使用$apply把要延迟执行的函数集成到digest生命周期。
但在Angular中还有一种延迟代码的方式,那就是Scope上的$evalAsync函数。$evalAsync接受一个函数,把它列入计划,在当前正持续的digest中或者下一次digest之前执行。举例来说,你可以在一个监听器的监听函数中延迟执行一些代码,即使它已经被延迟了,仍然会在现有的digest遍历中被执行。
我们首先需要的是存储$evalAsync列入计划的任务,可以在Scope构造函数中初始化一个数组来做这事:
function Scope() { this.$$watchers = []; this.$$asyncQueue = []; }
内容版权声明:除非注明,否则皆为本站原创文章。