使用髒檢查機制實現資料的雙向繫結
1.實現效果:
input標籤的值一變化,底下的p標籤的內容就跟著變化。
2.實現思路:
首先,angular得把我的舊資料記下來的吧。
angular的雙向繫結依賴髒檢查機制。為要雙向繫結的資料進行註冊,註冊到$scope上。($scope是angular資料和模型之間的橋樑。)註冊到$scope上的$$watchers陣列中。其實就是:我把我要監聽的物件,我要監聽的函式全部寫入一個記憶的數組裡,以後我要查詢我的資料是否改變,我就只需遍歷陣列,找出儲存在陣列中的old值,如果new值和old值不同,我就觸發數組裡的callback函式,來實現我想要的效果。這個陣列就是用來記憶我的舊資料和callback。
那麼angular使用什麼機制來查詢我的$$watcher陣列呢。
我們發現,在angular封裝的指令下,指令觸發時,angular會內部呼叫一個$apply函式。apply函式用來呼叫$rootScope下的$digest函式。(rootscope是所有scope們的根。一個個scope總得有個資料結構串在一起吧。要不然如何聯絡。)$degist從rootscope開始逐級遍歷各個scope,每個scope遍歷$$watchers陣列,查詢裡面的每一個old值和new值是不是相等呀,不相等就認為是髒資料,可以執行callback函式啦,並且更新old資料為new資料。每次遇到髒資料,angular就預測克鞥還會有髒資料,就會一直迴圈執行下去。
3.實現程式碼:
1.首先定義Scope類。
它有一個註冊監聽即新增到$$watchers陣列的功能Scope.prototype.$watch
,和一個觸發髒檢查的功能Scope.prototype.$digest
var Scope=function(){
this.$$watchers = [];
}
Scope.prototype.$watch=function(watchExp,listeners){
}
Scope.prototype.$digest=function(){
}
2.充實一下:
var Scope=function(){
this.$$watchers = [];
}
Scope.prototype.$watch=function(watchExp,listeners){
this.$$watchers.push({
watchExp:watchExp,
listeners:listeners||function(){}
})
}
Scope.prototype.$digest=function(){
var dirty;
do{
dirty=false;
for(var i = 0, len = this.$$watchers.length;i < len;i ++ ){
var newValue=this.$$watchers[i].watchExp();
var oldValue=this.$$watchers[i].last;
if(oldValue !== newValue){
dirty=true;
this.$$watchers[i].last=newValue;
this.$$watchers[i].listeners(newValue,oldValue);
}
}
}while(dirty);
}
3.html標籤
<input type="text" id="input">
<p id="display"></p>
4.例項化和呼叫
先例項化一波
var $scope=new Scope();
首先,我們在input標籤觸發onchange事件的時候,我們去$digist
一下$scope
的$$watcher
陣列,要找到我們要儲存的值。
那麼我們就要在初始化的時候,在$$watcher
裡面註冊一個值和它的watchEXP函式(這個用來拿到最新資料)和callback函式。
現在我們在scope裡定義一個name
變數。我們決定在watcher中監聽它,在onchange事件中維護它。
onchange中我們根據input的onchange事件,把input的值賦值給scope中的name
變數。(好了現在這個那麼更新了,就等$digist
檢查到它了。)
我們在watchEXP中返回new值:name
變數。我們在callback事件中(即digest事件要觸發的事件)把name值賦值給p的value。
$scope.name="liuchen";
var input=document.querySelector('#input');
var display=document.querySelector('#display');
input.onchange=function() {
$scope.name=input.value;
$scope.$digest();
}
$scope.$watch(function(){
return $scope.name
},function(){
display.innerText=$scope.name;
})
5.好難形容啊,看程式碼把
<input type="text" id="input">
<p id="display"></p>
<script>
var Scope=function(){
this.$$watchers = [];
}
Scope.prototype.$watch=function(watchExp,listeners){
this.$$watchers.push({
watchExp:watchExp,
listeners:listeners||function(){}
})
}
Scope.prototype.$digest=function(){
var dirty;
do{
dirty=false;
for(var i = 0, len = this.$$watchers.length;i < len;i ++ ){
var newValue=this.$$watchers[i].watchExp();
var oldValue=this.$$watchers[i].last;
if(oldValue !== newValue){
dirty=true;
this.$$watchers[i].last=newValue;
this.$$watchers[i].listeners(newValue,oldValue);
}
}
}while(dirty);
}
var $scope=new Scope();
$scope.name="liuchen";
var input=document.querySelector('#input');
var display=document.querySelector('#display');
input.onchange=function() {
$scope.name=input.value;
$scope.$digest();
}
$scope.$watch(function(){
return $scope.name
},function(){
display.innerText=$scope.name;
})
</script>
資料改了一個地方,卻要遍歷所有$$watchers
的所有資料,如果專案很大的話,效能是不是有點低下了呢。。。。。