scope與digest

scope的作用

  • 在controller和view之間打通數(shù)據(jù)
  • 在不同應(yīng)用的不同部分共享數(shù)據(jù)
  • 事件
  • 觀察數(shù)據(jù)變動(dòng)

scope的本質(zhì)

function Scope() {
}

var scope = new Scope();
scope.aProperty = 1;
expect(scope.aProperty).toBe(1);

$watch $digest

function Scope() {
    this.$$watchers = [];
}
Scope.prototype.$watch = function (watchFn, listenerFn) {
    var watcher = {
        watchFn: watchFn,
        listenerFn: listenerFn
    };
    this.$$watchers.push(watcher);
};
Scope.prototype.$digest = function () {
    this.$$watchers.forEach(function (watcher) {
        watcher.listenerFn();
    });
};

listenerFn執(zhí)行時(shí)機(jī)饭冬,dirty-checking崖技,當(dāng)watchFn返回值變化了,執(zhí)行l(wèi)istenerFn。原理是當(dāng)前和上一次比执虹,不一樣就認(rèn)為是dirty。

scope.counter = 0;
scope.$watch(function (scope) {
    return scope.someValue;
}, function (newValue, oldValue, scope) {
    scope.counter++;
});

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

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

scope.someValue = 'b';
expect(scope.counter).toBe(1);

實(shí)現(xiàn):

Scope.prototype.$digest = function () {
    var self = this;
    var newValue, oldValue;
    this.$$watchers.forEach(function (watcher) {
        newValue = watcher.watchFn(self);
        oldValue = watcher.last;
        if (newValue !== oldValue) {
            watcher.last = newValue;
            watcher.listenerFn(newValue, oldValue, self);
        }
    });
};
  • $digest遍歷的不是scope上的屬性甸祭,而是遍歷$$watchers數(shù)組
  • 每次$digest都會(huì)把$$watchers中的每一個(gè)watchFn都執(zhí)行至少一次呢蔫。

解決初始值undefined的問(wèn)題

scope.counter = 0;
scope.$watch(function (scope) {
    return scope.someValue;
}, function (newValue, oldValue, scope) {
    scope.counter++;
});

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

實(shí)現(xiàn):

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

初始值觸發(fā)的,newValue和oldValue的問(wèn)題

Scope.prototype.$digest = function () {
    var self = this;
    var newValue, oldValue;
    this.$$watchers.forEach(function (watcher) {
        newValue = watcher.watchFn(self);
        oldValue = watcher.last;
        if (newValue !== oldValue) {
            watcher.last = newValue;
            watcher.listenerFn(newValue,
                oldValue === initWatchVal ? newValue : oldValue,
                self);
        }
    });
};

解決不設(shè)置listenerFn的問(wèn)題

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

解決調(diào)用鏈的問(wèn)題

scope.name = 'Jane';
scope.$watch(function (scope) {
    return scope.nameUpper;
}, function (newValue, oldValue, scope) {
    if (newValue) {
        scope.initial = newValue.substring(0, 1) + '.';
    }
});
scope.$watch(function (scope) {
    return scope.name;
}, function (newValue, oldValue, scope) {
    if (newValue) {
        scope.nameUpper = newValue.toUpperCase();
    }
});

scope.$digest();
expect(scope.initial).toBe('J.');

解決:

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

解決監(jiān)聽(tīng)的死循環(huán)

scope.counterA = 0;
scope.counterB = 0;

scope.$watch(function (scope) {
    return scope.counterA;
}, function (newValue, oldValue, scope) {
    scope.counterB++;
});

scope.$watch(function (scope) {
    return scope.counterB;
}, function (newValue, oldValue, scope) {
    scope.counterA++;
});

TTL(Time To Live)

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

能否減少循環(huán)次數(shù)鹏氧?

function Scope() {
    this.$$watchers = [];
    this.$$lastDirtyWatch = null;
}
Scope.prototype.$digest = function () {
    var ttl = 10;
    var dirty;
    this.$$lastDirtyWatch = null;
    do {
        dirty = this.$$digestOnce();
        if (dirty && !(ttl--)) {
            throw "10 digest iterations reached";
        }
    } while (dirty);
};
Scope.prototype.$$digestOnce = function () {
    var self = this;
    var newValue, oldValue, dirty;
    forEach(this.$$watchers, function (watcher) {
        newValue = watcher.watchFn(self);
        oldValue = watcher.last;
        if (newValue !== oldValue) {
            self.$$lastDirtyWatch = watcher;
            watcher.last = newValue;
            watcher.listenerFn(newValue,
                (oldValue === initWatchVal ? newValue : oldValue),
                self);
            dirty = true;
        } else if (self.$$lastDirtyWatch === watcher) {
            return false;
        }
    });
    return dirty;
};

$watch嵌套問(wèn)題

scope.aValue = 'abc';
scope.counter = 0;
scope.$watch(
    function (scope) {
        return scope.aValue;
    },
    function (newValue, oldValue, scope) {
        scope.$watch(
            function (scope) {
                return scope.aValue;
            },
            function (newValue, oldValue, scope) {
                scope.counter++;
            }
        );
    }
);

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

解決方案:

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

臟檢查中的引用檢查和值檢查

臟檢查中的NaN問(wèn)題

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末渤涌,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子把还,更是在濱河造成了極大的恐慌实蓬,老刑警劉巖茸俭,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異安皱,居然都是意外死亡调鬓,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門(mén)练俐,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)袖迎,“玉大人,你說(shuō)我怎么就攤上這事腺晾⊙嘧叮” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵悯蝉,是天一觀的道長(zhǎng)归形。 經(jīng)常有香客問(wèn)我,道長(zhǎng)鼻由,這世上最難降的妖魔是什么暇榴? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮蕉世,結(jié)果婚禮上蔼紧,老公的妹妹穿的比我還像新娘。我一直安慰自己狠轻,他們只是感情好奸例,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著向楼,像睡著了一般查吊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上湖蜕,一...
    開(kāi)封第一講書(shū)人閱讀 51,198評(píng)論 1 299
  • 那天逻卖,我揣著相機(jī)與錄音,去河邊找鬼昭抒。 笑死评也,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的灭返。 我是一名探鬼主播盗迟,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼婆殿!你這毒婦竟也來(lái)了诈乒?” 一聲冷哼從身側(cè)響起罩扇,我...
    開(kāi)封第一講書(shū)人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤婆芦,失蹤者是張志新(化名)和其女友劉穎怕磨,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體消约,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡肠鲫,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了或粮。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片导饲。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖氯材,靈堂內(nèi)的尸體忽然破棺而出渣锦,到底是詐尸還是另有隱情,我是刑警寧澤氢哮,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布袋毙,位于F島的核電站,受9級(jí)特大地震影響冗尤,放射性物質(zhì)發(fā)生泄漏听盖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一裂七、第九天 我趴在偏房一處隱蔽的房頂上張望皆看。 院中可真熱鬧,春花似錦背零、人聲如沸腰吟。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)蝎困。三九已至,卻和暖如春倍啥,著一層夾襖步出監(jiān)牢的瞬間禾乘,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工虽缕, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留始藕,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓氮趋,卻偏偏與公主長(zhǎng)得像伍派,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子剩胁,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

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