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

相關文章
相關標籤/搜索