国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

Angular 學習筆記:$digest 實現(xiàn)原理

baiy / 2999人閱讀

摘要:前綴表示私有變量上述代碼實現(xiàn)的并不實用,因為實際上我們需要的是監(jiān)聽的對象數(shù)據(jù)發(fā)生改變時才執(zhí)行相應(yīng)的方法。我們使用來約束遍歷的最大次數(shù),在中默認次數(shù)為。

$watch 和 $digest

$watch$digest 是數(shù)據(jù)綁定中的核心概念:我們可以使用 $watch 在 scope 中綁定 watcher 用于監(jiān)聽 scope 中發(fā)生的變化,而 $digest 方法的執(zhí)行即是遍歷 scope 上綁定的所有 watcher,并執(zhí)行相應(yīng)的 watch(指定想要監(jiān)控的對象) 和 listener(當數(shù)據(jù)改變時觸發(fā)的回調(diào)) 方法。

function Scope {
    this.$$watchers = []; // $$ 前綴表示私有變量
}

Scope.prototye.$watch = function(watchFn, listenerFn) {
    let watcher = {
        watchFn: watchFn,
        listenerFn: listenerFn,
    };

    this.$$watchers.push(watcher);
}

Scope.prototype.$digest = function() {
    this.watchers.forEach((watcher) => {
        watcher.listenerFn();
    });
}

上述代碼實現(xiàn)的 $digest 并不實用,因為實際上我們需要的是:監(jiān)聽的對象數(shù)據(jù)發(fā)生改變時才執(zhí)行相應(yīng)的 listener 方法。

臟檢查
Scope.prototype.$digest = function() {
    let self = this;
    let newValue, oldValue;
    this.watchers.forEach((watcher) => {
        newValue = watcher.watchFn(self);
        oldValue = watcher.last;
        if (newValue !== oldValue) {
            watch.last = newValue;
            watcher.listenerFn(newValue, oldValue, self);
        }
    });
}

上述代碼在大部分情況下可以正常運行,但是當我們首次遍歷 watcher 對象時其 last 變量值為 undefined,這樣會導致如果 watcher 的第一個有效值同為 undefined 不會觸發(fā) listener 方法。

console.log(undefined === undefined) // true

我們使用 initWatchVal 方法解決這個問題.

function initWatchVal() {
  // TODO
}

Scope.prototye.$watch = function(watchFn, listenerFn) {
  let watcher = {
      watchFn: watchFn,
      listenerFn: listenerFn || function() {},
      last: initWatchVal
  };

  this.$$watchers.push(watcher);
}

Scope.prototype.$digest = function() {
  let self = this;
  let newValue, oldValue;
  this.watchers.forEach((watcher) => {
      newValue = watcher.watchFn(self);
      oldValue = watcher.last;
      if (newValue !== oldValue) {
          watch.last = newValue;
          watcher.listenerFn(newValue, oldValue === initWatchVal ? newValue : oldValue, self);
      }
  });
}
循環(huán)進行臟檢查

在進行 digest 時往往會發(fā)生如下情況,即某個 watcher 執(zhí)行 listener 方法會引起其他 watcher 監(jiān)聽的對象數(shù)據(jù)發(fā)生改變,因此我們需要循環(huán)進行臟檢查來使變化“徹底”完成。

Scope.prototype.$$digestOnce = function() {
    let self = this;
    let newValue, oldValue, dirty;
    this.watchers.forEach((watcher) => {
        newValue = watcher.watchFn(self);
        oldValue = watcher.last;
        if (newValue !== oldValue) {
            dirty = true;
            watch.last = newValue;
            watcher.listenerFn(newValue, oldValue === initWatchVal ? newValue : oldValue, self);
        }
    });
    
    return dirty;
}

Scope.prototype.$digest = function() {
    let dirty;
    do { dirty = this.$$digestOnce(); }
    while (dirty);
}

上述代碼只要在遍歷中發(fā)現(xiàn)臟值,就會多循環(huán)一輪直到?jīng)]有發(fā)現(xiàn)臟值為止,我們考慮這樣的情況:即是兩個 watcher 之間互相影響彼此,則會導致無限循環(huán)的問題。

我們使用 TTL(Time to Live)來約束遍歷的最大次數(shù),在 Angular 中默認次數(shù)為10。

Scope.prototype.$digest = function() {
    let dirty;
    let ttl = 10;
    do {
        dirty = this.$$digestOnce();
        if (dirty && !(ttl--)) {
            throw "10 digest iterations reached.";
        }
    } while (dirty)
}

同時,在每次 digest 的最后一輪遍歷沒有必要對全部 watcher 進行檢查,我們通過使用 $$lastDirtyWatch 變量來對這部分代碼的性能進行優(yōu)化。

function Scope {
    this.$$watchers = [];
    this.$$lastDirtyWatch = null;
}

Scope.prototype.$digest = function() {
    let dirty;
    let ttl = 10;

    this.$$lastDirtyWatch = null;

    do {
        dirty = this.$$digestOnce();
        if (dirty && !(ttl--)) {
            throw "10 digest iterations reached.";
        }
    } while (dirty)
}

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

    return dirty;
}

同時為了避免 $watch 嵌套使用帶來的不良影響,我們需要在每次添加 watcher 時重置 $$lastDirtyWatch:

Scope.prototye.$watch = function(watchFn, listenerFn) {
    let watcher = {
        watchFn: watchFn,
        listenerFn: listenerFn || function() {},
        last: initWatchVal
    };

    this.$$watchers.push(watcher);
    this.$$lastDirtyWatch = null;
}
深淺臟檢查

目前為止我們實現(xiàn)的臟檢查,僅能監(jiān)聽到值的變化(淺臟檢查),無法判斷引用內(nèi)部數(shù)據(jù)發(fā)生的變化(深臟檢查)。

Scope.prototye.$watch = function(watchFn, listenerFn, valueEq) {
    let watcher = {
        watchFn: watchFn,
        listenerFn: listenerFn || function() {},
        valueEq: !!valueEq,
        last: initWatchVal
    };

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

  return dirty;
}
NaN 的兼容考慮

需要注意的是,NaN 不等于其自身,所以在判斷 newValue 與 oldValue 是否相等時,需要特別考慮。

Scope.prototype.$$areEqual = function(newValue, oldValue, valueEq) {
    if (valueEq) {
        return _.isEqual(newValue, oldValue);
    } else {
        return newValue === oldValue ||
            (typeof newValue === "number" && typeof oldValue === "number" && isNaN(newValue) && isNaN(oldValue));
    }
}

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/84389.html

相關(guān)文章

  • [譯] $digestAngular 中重生

    摘要:但如果一個組件在生命周期鉤子里改變父組件屬性,卻是可以的,因為這個鉤子函數(shù)是在更新父組件屬性變化之前調(diào)用的注即第步,在第步之前調(diào)用。 原文鏈接:Angular.js’ $digest is reborn in the newer version of Angular showImg(https://segmentfault.com/img/remote/146000001468785...

    incredible 評論0 收藏0
  • [譯] 關(guān)于 `ExpressionChangedAfterItHasBeenCheckedErro

    摘要:本文將解釋引起這個錯誤的內(nèi)在原因,檢測機制的內(nèi)部原理,提供導致這個錯誤的共同行為,并給出修復這個錯誤的解決方案。這一次過程稱為。這個程序設(shè)計為子組件拋出一個事件,而父組件監(jiān)聽這個事件,而這個事件會引起父組件屬性值發(fā)生改變。 原文鏈接:Everything you need to know about the ExpressionChangedAfterItHasBeenCheckedE...

    andong777 評論0 收藏0
  • angular1學習筆記,view model 的同步過程

    摘要:但實際上這時程序并沒有計算手續(xù)費。經(jīng)過排查并查閱文檔之后,發(fā)現(xiàn)是的問題。本文沒有具體介紹和管道,關(guān)于這部分可以參考文中給出的鏈接 事情起源于在項目中遇到的一個小問題:項目中需要一個輸入框輸入賣出產(chǎn)品數(shù)量,并且在用戶輸入后根據(jù)輸入數(shù)據(jù)計算手續(xù)費。很自然的我用了ng-model和ng-change,并且一般情況下沒什么問題。問題是:輸入框下還有一個按鈕是全部賣出,點擊這個按鈕程序會自動設(shè)置...

    Forelax 評論0 收藏0
  • 面試題總結(jié)

    摘要:異步管理等到執(zhí)行完成后返回種狀態(tài),代表成功,代表失敗。我們在函數(shù)內(nèi)聲明的變量叫局部變量,局部變量只能在里面訪問,外面是訪問不到的。那么就是為解決這一問題的??梢杂面準綄懛ǖ鹊疆惒接薪Y(jié)果再進行下一步。 1. vue的雙向綁定原理: vue的雙向綁定原理是通過Object.definedProperty的getter和setter來對屬性進行數(shù)據(jù)劫持的。 因為Object.definedP...

    weizx 評論0 收藏0
  • 面試題總結(jié)

    摘要:異步管理等到執(zhí)行完成后返回種狀態(tài),代表成功,代表失敗。我們在函數(shù)內(nèi)聲明的變量叫局部變量,局部變量只能在里面訪問,外面是訪問不到的。那么就是為解決這一問題的??梢杂面準綄懛ǖ鹊疆惒接薪Y(jié)果再進行下一步。 1. vue的雙向綁定原理: vue的雙向綁定原理是通過Object.definedProperty的getter和setter來對屬性進行數(shù)據(jù)劫持的。 因為Object.definedP...

    enali 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<