angularJS之$watch、$digest和$apply方法

  最近项目上使用了比较多的angular JS,一直都对它感受比较陌生,总以为有点反直觉,这段时间,准备下定决心弄明白,这个框架究竟是怎么一回事,以及它的工做原理,生命周期……一点一点的啃完它吧。首先,让咱们先来看看$watch、$digest、$apply这三个方法吧!html

  • $watch(watchExpression, listener, objectEquality) 
Param Type Details
watchExpression

function()angularjs

stringexpress

Expression that is evaluated on each $digest cycle. A change in the return value triggers a call to the listener.
  • string: Evaluated as expression
  • function(scope): called with current scope as a parameter.
listener
(optional)

function()api

string 浏览器

Callback called whenever the return value of the watchExpressionchanges.
  • string: Evaluated as expression
  • function(newValue, oldValue, scope): called with current and previous values as parameters.
objectEquality
(optional)
boolean Compare object for equality rather than for reference. 

     从表格中能够看到,watchExpression和listener能够是一个string,也能够是一个function(scope)。该表达式在每次调用了$digest方法以后都会从新算值,若是返回值发生了改变,listener就会执行。在判断newValue和oldValue是否相等时,会递归的调用angular.equals方法。在保存值以备后用的时候调用的是angular.copy方法。listener在执行的时候,可能会修改数据从而触发其余的listener或者本身直到没有检测到改变为止。Rerun Iteration的上限是10次,这样可以保证不会出现死循环的状况。
     $watch的基本结构以下:app

//$scope.$watch(<function/expression>, <handler>);
$scope.$watch('foo', function(newVal, oldVal) {
    console.log(newVal, oldVal);
});
//or
$scope.$watch(function() {
    return $scope.foo;
}, function(newVal, oldVal) {
    console.log(newVal, oldVal);
});
  • $digest()

     该方法会触发当前scope以及child scope中的全部watchers,由于watcher的listener可能会改变model,因此$digest方法会一直触发watchers直到再也不有listener被触发。固然这也有可能会致使死循环,不过angular也帮咱们设置了上限10!不然会抛出“Maximum iteration limit exceeded.”。
     一般,咱们不在controller或者directive中直接调用$digest方法,而是调$apply方法,让$apply方法去调用$digest方法。
     如何调用该方法呢?框架

$scope.$digest();
  • $apply(exp)
Param Type Details
exp
(optional)

string异步

function() ide

An angular expression to be executed.
  • string: execute using the rules as defined in expression.
  • function(scope): execute the function with current scope parameter.

    我的理解,$apply方法就是将$digest方法包装了一层,exp是可选参数,能够是一个string,也能够是function(scope)。伪代码(来自官方文档)以下:测试

function $apply(expr) {
    try {
        return$eval(expr);
    } catch(e) {
        $exceptionHandler(e);
    } finally {
        $root.$digest();
    }
}

     $apply方法使得咱们能够在angular里面执行angular框架以外的表达式,好比说:浏览器DOM事件、setTimeout、XHR或其余第三方的库。因为咱们要在angular框架内调用,咱们必须得准备相应的scope。调用方式以下:

$scope.$apply('foo = "test"');
//or
$scope.$apply(function(scope) {
    scope.foo = 'test';
});
//or
$scope.$apply(function(){
    $scope.foo = 'test';
});
  • $watch、$digest、$apply是如何与视图的更新相关联的呢?
  1. directive给$scope上的一个model注册$watch来监视它的变化,listener会去更新DOM元素的值。
  2. directive给DOM中的一些元素注册event handler,它们会取得DOM中元素的值,而后更新到$scope上的model中去。它也会触发$apply或者$digest。
  3. 当你经过框架更新了$scope上model的值,好比说:$http.get(),当它完成后也会触发$digest方法。
  4. $digest会去检查directive注册的$watch,发现值被修改就会触发相关联的handler,而后更新DOM元素。

     至于angular js为何要这么作,请看我上一篇博客angular js之scope.$apply方法

  • $watch
  1. 当$scope上的值发生变化时,尽可能在directive中使用$watch去更新DOM。
  2. 尽可能不要再controller中使用$watch方法,它会增长测试的复杂度,并且也没必要要。可使用scope上的方法去更新被改变的值。
  • $digest、$apply
  1. 在directive中使用$digest/$apply使angular知道一个异步请求完成后的变化,好比说DOM Event。
  2. 在service中使用$digest/$apply使angular知道一个异步操做已经完成,好比说WebSocket、或者第三方的库。
  3. 尽可能不要再controller中使用$digest/$apply,这样的话测试起来会比较困难。

 

===============================================================================

  • 关于angular.equals方法

     该方法支持value types,regular expressions、arrays、objects。官方文档写的很清楚:
Two objects or values are considered equivalent if at least one of the following is true:

  1. Both objects or values pass === comparison.
  2. Both objects or values are of the same type and all of their properties are equal by comparing them with angular.equals.
  3. Both values are NaN. (In JavaScript, NaN == NaN => false. But we consider two NaN as equal)
  4. Both values represent the same regular expression (In JavasScript, /abc/ == /abc/ => false. But we consider two regular expressions as equal when their textual representation matches).

During a property comparison, properties of function type and properties with names that begin with $ are ignored.
Scope and DOM Window objects are being compared only by identify (===).

相关文章
相关标签/搜索