1. 程式人生 > >使用髒檢查機制實現資料的雙向繫結

使用髒檢查機制實現資料的雙向繫結

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的所有資料,如果專案很大的話,效能是不是有點低下了呢。。。。。