你們都是到angularJS中很是屌的一個性能,數據雙向綁定,這就意味着view中的數據發生任何變化的時候,這個變化也會相應的反映到scope上,也就是說scope的模型會動態更新。因此有時候咱們的需求多是要監控摸個model的變化,下面就簡單的說下這個操做以及實現原理html
$watch
$watch能夠很方便且簡單的監控一個model的變化,舉個栗子:java
<html> <head> <script src='./lib.angular.min.js'></script> </head> <body ng-app='watch'> <input ng-model='name' type='text'/> <div>change count: {{count}}</div> <script> angular.module('watch',[]) .run(['$rootScope',function($rootScope){ $rootScope.count = 0; $rootScope.name = 'Alfred'; $rootScope.$watch('name',function(){ $rootScope.count++; }) }]); </script> </body> </html>
上面這個代碼就是用來監控name的變化的,每次當咱們在input輸入框中輸入一個值的時候,$rootScope中的count就會對應的+1;數組
在angularJS的內部,每當咱們對name的值進行修改的時候,angularJS內部中的$digest就會被調用一次,(下面在詳細的說$digest的原理),並在運行結束以後檢查咱們用$watch來監控的模型,如何和上一次執行$digest以前相比發生變化了,則執行$watch中的回調函數。app
然而!!!在咱們實際的開發中,僅僅實現對一個原始類型的數據監控是遠遠不可以知足所需的,對於原始類型的數據,若是咱們使用了一次賦值操做,則這個原始類型的數據變量會真正的別賦值一次,然而對於引用類型的變量,進行賦值時,僅僅是將賦值的變量指向了這個引用類型。
能夠看下下面的栗子:異步
<html> <head> <script src='./lib.angular.min.js'></script> </head> <body ng-app='watch'> <div ng-repeat='item in items'> <input ng-model='item.a'/><span>{{item.a}}</span> </div> <div>change count: {{count}}</div> <script> angular.module('watch',[]) .run(['$rootScope',function($rootScope){ $rootScope.count = 0; $rootScope.items = [ { "a": 1 }, { "a": 2 }, { "a": 3 }, { "a": 4 } ] $rootScope.$watch('items',function(){ $rootScope.count++; }) }]); </script> </body> </html>
在這個栗子中咱們就會發現,無論咱們怎麼改變其中的值,count都不會發生變化的。而這個就是咱們上面說那樣,在說明這個以前,咱們在說明下$watch的第三個參數,通常$watch函數的前兩個參數是必傳的(監控對象,回調函數),第三個參數默認爲false,這樣的話咱們進行的監控叫作引用監控,這個意思就是監控對象的應用沒有發生變化的時候就不算對象發生了變化,具體的來講,上面的例子,就算items的屬性發生了變化,只要items的引用沒有發生變化,$watch就都當作沒有看見,可是好比講一個數組賦值給items時,這個時候$watch就看不下去了(給你陽光你就燦爛了還)。
相反,若是第三個參數設置爲true的時候,那麼咱們的監控叫作「全等監控」,此時的$watch的要求就是比較苛刻了,只要他監控的對象有一點點變化時,$watch就會跳出來,臥槽!你竟然還敢動!!!函數
固然值得提一下的是:爲何第三個參數加個true,這麼方便了咱們還不加呢?!固然是牽涉到性能的問題啦!全等監控運行起來的時候是先監控到整個對象,而後在每一次吧$diges跑起來以前先用angualr.copy()將整個對象先拷貝一遍以後再調用angular.equal()方法來進行比較,因此這一監控可能會消耗大量的資源!性能
在angularJS 1.1.4又出來了一個$watchCollection()方法,專門來監控數組集合的,他的性能介於引用監控和全等監控之間,它不會對數組的每一項內容 進行監控,而是當數組的pop和push時候作出反應。具體的栗子咱就很少贅述了。ui
下面談一談$apply
你們都知道他的做用是把改變同步綁定到界面上,可是它爲何存在呢?何時須要用它呢?何時又不須要呢?
那麼咱們首先說一下angular是如何進行數據雙向綁定的吧。
要知道一個變量變了,方法無非就兩種
一、 經過固定的接口,好比set,get方法,經過set設置變量的值,set被調用時作個比較就能夠,可是這個方法和複雜!
二、 髒檢查,將某一個對象複製一份快照,在某個時間,比較如今對象與快照的值。很明顯,這個方法要複製兩份對象,並且要遍歷對象,比較每個屬性。對!這樣的確有性能問題!可是angular就是用這個的~
可是人家angular的髒檢查不是對全部對象進行檢查,只是當對象綁定到html中,該對象才複合檢查對象(watcher),同理,angular對屬性的髒檢查也是如此。
看下watcher對象的源代碼咱們就知道了:spa
watcher = { fn: listener, //監聽回調函數 last: initWatchVal, //上一狀態值 get: get, //取得監聽的值 exp: watchExp, //監聽表達式 eq: !!objectEquality //要不要比較引用 };
那麼咱們何時去進行髒檢查呢?
髒檢查的點是在函數執行完以後,可是不標明異步調用也執行完畢,若是咱們的功能是異步的,那麼咱們會發現咱們的改變並無更新到DOM上。
舉個栗子:雙向綁定
<!-- lang: js --> function Ctrl($scope) { $scope.message = "Waiting 2000ms for update"; setTimeout(function () { $scope.message = "Timeout called!"; // AngularJS unaware of update to $scope }, 2000); }
簡單說dom上永遠都不會顯示Timeout called
固然,這個就是咱們$apply的應用場景了,調用它,手動觸發髒檢查,舉個例子:angularJs提供了$timeout,爲何咱有了setTimeout還要提供這個呢?就是應爲$timeout異步完成後,angularJs會自動觸發$apply
下面說下$apply的使用注意事項吧
在一個具備$apply的環境中使用apply,會拋出異常來的
因此若是說咱們的代碼不在$apply環境中,結構是異步返回的,咱們就須要手動觸發$apply
Apply接受一個函數做爲參數,函數中別綁定的對象會被髒檢查,function不能是異步的!固然,當apply函數的參數爲空的是時候,它會把當前做用域中全部的髒對象都檢查一遍。浪費性能!
然而這個髒檢查有事怎麼檢查的呢??
好吧,這個就說到$digest函數了
$apply被調用後最終都會觸發$digest()
在調用了 $scope.$digest() 後, $digest 循環就開始了。假設你在一個 ng-click 指令對應的 handler 函數中更改了 scope 中的一條數據,此時 AngularJS 會自動地經過調用 $digest() 來觸發一輪 $digest 循環。當 $digest 循環開始後,它會觸發每一個 watcher 。這些 watchers 會檢查 scope 中的當前 model 值是否和上一次計算獲得的 model 值不一樣。若是不一樣,那麼對應的回調函數會被執行。調用該函數的結果,就是 view 中的表達式內容 ( 譯註:諸如 {{ aModel }}) 會被更新。除了 ng-click 指令,還有一些其它的 built-in 指令以及服務來讓你更改 models( 好比 ng-model , $timeout 等 ) 和自動觸發一次 $digest 循環
暫且就說這麼多吧,說多了也迷糊,不少都是須要本身去實際應用和體會的