創(chuàng)建你自己的AngularJS-第一部分 Scopes (3)

基于值的Dirty檢查

目前為止我們已經(jīng)使用嚴格的相等操作符===來比較新老值榄棵。在大多數(shù)情況下這樣就已經(jīng)是比較好的钓瞭,因為它能檢測所有基本類型的變化同時能檢測一個對象或數(shù)組是否變成另一個新的對象或數(shù)組。但是Angular還有另外一種方式用來檢測對象或者數(shù)組內(nèi)部的某個屬性或者元素的變化憨琳。換句話說就是你可以監(jiān)視值的變化演侯,而不僅僅只有引用第租。

這種dirty檢查是通過在$watch函數(shù)中提供一個額外的可選布爾標志而達到的。當標志為true時般眉,基于值的檢測被啟用了赵。讓我們來添加一個測試:

test/scope_spec.js


it("conpares based on value if enabled",function(){
  scope.aValue = [1,2,3];
  scope.counter = 0 ;

  scope.$watch(
    function(scope){ return scope.aValue; }
    function(newValue,oldValue,scope){
      scope.counter++;
    },
    true
  );

  scope.$digest();
  expect(scope.counter).toBe(1);

  scope.aValue.push(4);
  scope.$digest();
  expect(scope.counter).toBe(2);
});

在這個測試里面的無論scope.aValue數(shù)組什么時候被改變計數(shù)器都會增長。當我們?yōu)閿?shù)組里面增加一個元素時甸赃,我們期望它能夠注意到這個變化柿汛,但是實際上它并沒有。scope.aValue依然是同一個數(shù)組埠对,只是里面的內(nèi)容發(fā)生了變化(引用沒變络断,值發(fā)生了變化)。

首先讓我們重新定義$watch為其添加布爾值的標志位并且把它存儲在監(jiān)視器中:

src/scope.js


Scope.prototype.$watch = function(watchFn,listenerFn,valueEq){
  var watcher = {
    watchFn : watchFn,
    listenerFn : listenerFn || function(){},
    valueEq : !!valueEq,
    last : initWatchVal
  };
  this.$$watchers.push(watcher);
  this.$$lastDirtyWatch = null;
};

在上面代碼中项玛,我們所做的就是在監(jiān)視器中添加布爾標志位貌笨,并通過兩次的取反操作強制確保它必為一個布爾值。當一個用戶調(diào)用$watch而沒有創(chuàng)第三個參數(shù)時襟沮,valueEq將會是undefined,而通過兩次取反它將變成false锥惋。

基于值的dirty檢查意味著如果舊值或者新值是對象或者數(shù)組我們就必須迭代它們所包含的所有屬性或者元素昌腰。如果它們的值有任何的不同之處那么監(jiān)視器就是dirty的。如果值包含另外一個對象或者數(shù)組净刮,那么這個對象或數(shù)組將遞歸似的比較值剥哑。

Angular使用它自己的比較檢查函數(shù),但是我們將使用Lo-Dash里面提供的函數(shù)來進行代替淹父,因為它已經(jīng)實現(xiàn)了我們想要的功能株婴。讓我定義一個新的函數(shù)它使用兩個值和一個布爾標識作為參數(shù),并將兩個值進行比較:

src/scope.js


Scope.prototype.$$areEqual = function(newValue,oldValue,valueEq){
  if(valueEq){
    return _.isEqual(newValue,oldValue);
  }else{
    return newValue === oldValue;
  }
}

為了注意到值的變化暑认,我們還需要改變在各個監(jiān)視器中存儲舊值的方式困介。只存儲當前值的引用顯然已經(jīng)不夠了,因為任何在值上的改變也將應用于我們的監(jiān)視中蘸际。我們永遠不會注意到任何更改因為$$areEqual從一開始就將得到兩個引用座哩,并且這兩個引用的值并不會發(fā)生改變。由于這個原因我們需要對值進行深拷貝并將其存儲粮彤。

就像Angular使用它自己的深拷貝函數(shù)進行相等的檢查根穷,現(xiàn)在我們將使用Lo-Dash的一個方法來實現(xiàn)。

讓我們更新$digestOnce讓它使用新的$$areEqual函數(shù)并拷貝最后一個引用:

src/scope.js


Scope.prototype.$$digestOnce = function(){
  var self = this;
  var newValue,oldValue,dirty;
  
  _.forEach(this.$$watchers,function(watcher){
    newValue = watcher.watchFn(self);
    oldValue = watcher.last;
    if(!self.$$areEqual(newValue,oldValue,watcher.valueEq)){
      self.$$lastDirtyWatch = watcher;
      watcher.last = (watcher.valueEq ? _.cloneDeep(newValue) : newValue);
      watcher.listenerFn(newValue,
        (oldValue === initWatchVal ? newValue : oldValue),
        self);
        dirty = true;
    }else if(self.$$lastDirtyWatch === watcher){
      return false;
    }
  });
  return dirty;
}

測試通過导坟,現(xiàn)在我們的代碼已經(jīng)支持兩種相等檢查了屿良。

檢查值的操作顯然比檢查引用的操作更為復雜。涉及到的內(nèi)容也更多惫周。檢查一個嵌套數(shù)據(jù)所花費的時間尘惧,進行一次深拷貝所占用的內(nèi)存大小。這就是為什么Angular沒有默認進行基于值的dirty檢查递递。你需要顯式地設置標志來啟用它喷橙。

Angular還有第三種dirty檢查機制:集合監(jiān)視("Collection Watching")。我們將在第3章的時候?qū)崿F(xiàn)它登舞。

在進行值的比較之前贰逾,還有一個JavaScript所獨有的問題需要我們進行處理。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末菠秒,一起剝皮案震驚了整個濱河市似踱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌稽煤,老刑警劉巖核芽,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異酵熙,居然都是意外死亡轧简,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門匾二,熙熙樓的掌柜王于貴愁眉苦臉地迎上來哮独,“玉大人拳芙,你說我怎么就攤上這事∑よ担” “怎么了舟扎?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長悴务。 經(jīng)常有香客問我睹限,道長,這世上最難降的妖魔是什么讯檐? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任羡疗,我火速辦了婚禮,結(jié)果婚禮上别洪,老公的妹妹穿的比我還像新娘叨恨。我一直安慰自己,他們只是感情好挖垛,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布痒钝。 她就那樣靜靜地躺著,像睡著了一般痢毒。 火紅的嫁衣襯著肌膚如雪送矩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天闸准,我揣著相機與錄音,去河邊找鬼梢灭。 笑死夷家,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的敏释。 我是一名探鬼主播库快,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼钥顽!你這毒婦竟也來了义屏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蜂大,失蹤者是張志新(化名)和其女友劉穎闽铐,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體奶浦,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡兄墅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了澳叉。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片隙咸。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡沐悦,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出五督,到底是詐尸還是另有隱情藏否,我是刑警寧澤,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布充包,位于F島的核電站副签,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏误证。R本人自食惡果不足惜继薛,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望愈捅。 院中可真熱鬧遏考,春花似錦、人聲如沸蓝谨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽譬巫。三九已至咖楣,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間芦昔,已是汗流浹背诱贿。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留咕缎,地道東北人珠十。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像凭豪,于是被迫代替她去往敵國和親焙蹭。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內(nèi)容