Angularjs-Dirty Checking


Angularjs實現了數據雙向綁定,就像下面這樣:

<!doctype html>
<htnl ng-app>
<head>
    <script src="js/angular.js"></script>
</head>
<body>
    <input type="text" ng-model="name">
    <h2>Hello {{name}}</h2>
</body>
</html>

這使得在View和Model在變化的時候,都會更新對方:

Angularjs是通過DirtyChecking實現的Two-Way Data Binding:

$scope.$apply:當控制器或指令在Angularjs運行時,Angularjs內部會運行$scope.$apply函數,這個函數會接收一個函數參數並且運行它,在這之后才會在rootScope上運行$digest.

$digest函數將會在$rootScope中被$scope.$apply所調用。它將會在$rootScope中運行digest循環,然后向下遍歷每一個作用域並在每個作用域上運行循環。在簡單的情形中,digest循環將會觸發所有位於$$watchers變量中的所有watchExp函數,將它們和最新的值進行對比,如果值不相同,就會觸發監聽器。

當digest循環運行時,它將會遍歷所有的監聽器然后再次循環,只要這次循環發現了”臟值”,循環就會繼續下去。如果watchExp的值和最新的值不相同,那么這次循環就會被認為發現了臟值。理想情況下它會運行一次,如果它運行超10次,會看到一個錯誤。

因此當$scope.$apply運行的時候,$digest也會運行,它將會循環遍歷$$watchers,只要發現watchExp和最新的值不相等,變化觸發事件監聽器。 在AngularJS中,只要一個模型的值可能發生變化,$scope.$apply就會運行。這就是為什么當你在AngularJS之外更新$scope時,例如在一個setTimeout函數中,你需要手動去運行$scope.$apply():這能夠讓AngularJS意識到它的作用域發生了變化。 

$scope.$watch:為了監視一個變量的變化,可以使用$scope.$watch函數。這個函數有三個參數,它指明了”要觀察什么”(watchExp),”在變化時要發生什么”(listener),以及要監視的是一個變量還是一個對象。當在檢查一個參數時,可以忽略第三個參數。$watch的第一個參數可以是字符串,也可以是函數。

$$watchers:$scope中的$$watchers變量保存着所定義的所有的監視器。如果在控制台中打印$$watchers,會發現它是一個對象數組。$watch函數將會返回一個deregisterWatch函數。這意味着如果使用$scope.$watch對一個變量進行監視,也可以在以后通過調用某個函數來停止監視。

 

簡化版的Dirty-Checking:

HTML:

<input type="text" />

<a href="#" onclick="updateScopeValue();">Set input value to Bob</a>

Javascript:

var Scope = function( ) {
    this.$$watchers = [];    
};

Scope.prototype.$watch = function( watchExp, listener ) {
    this.$$watchers.push( {
        watchExp: watchExp,
        listener: listener || function() {}
    } );
};

Scope.prototype.$digest = function( ) {
    var dirty;

    do {
            dirty = false;

            for( var i = 0; i < this.$$watchers.length; i++ ) {
                var newValue = this.$$watchers[i].watchExp(),
                    oldValue = this.$$watchers[i].last;

                if( oldValue !== newValue ) {
                    this.$$watchers[i].listener(newValue, oldValue);

                    dirty = true;

                    this.$$watchers[i].last = newValue;
                }
            }
    } while(dirty);
};


var $scope = new Scope();

$scope.name = 'Ryan';

var element = document.querySelectorAll('input');

element[0].onkeyup = function() {
    $scope.name = element[0].value;

    $scope.$digest();
};

$scope.$watch(function(){
    return $scope.name;
}, function( newValue, oldValue ) {
    console.log('Input value updated - it is now ' + newValue);
    
    element[0].value = $scope.name;
} );

var updateScopeValue = function updateScopeValue( ) {
    $scope.name = 'Bob';
    $scope.$digest();
};

使用上面的代碼,無論何時改變了input的值,$scope中的name屬性都會相應的發生變化。這樣就實現了數據雙向綁定。

 參考:http://ryanclark.me/how-angularjs-implements-dirty-checking/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM