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


  最近項目上使用了比較多的angular JS,一直都對它感覺比較陌生,總覺得有點反直覺,這段時間,准備下定決心弄明白,這個框架到底是怎么一回事,以及它的工作原理,生命周期……一點一點的啃完它吧。首先,讓我們先來看看$watch、$digest、$apply這三個方法吧!

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

function()

string

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()

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的基本結構如下:

//$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()

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 (===).


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM