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)題