原版地址:http://code.angularjs.org/1.0.2/docs/guide/scopejavascript
1、什麼是Scope?css
scope(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope)是一個指向應用model的object。它也是expression(http://www.cnblogs.com/lcllao/archive/2012/09/16/2687162.html)的執行上下文。scope被放置於一個相似應用的DOM結構的層次結構中。scope能夠監測(watch,$watch)expression和傳播事件。html
2、scope的特性java
3、Scope as Data-Model(scope做爲數據模型)angularjs
scope是在應用controller與view之間的紐帶。在模版linking(http://www.cnblogs.com/lcllao/archive/2012/09/04/2669802.html)的階段,directive(http://www.cnblogs.com/lcllao/archive/2012/09/09/2677190.html)在scope中設置$watch表達式。$watch讓directive可以得知屬性的變化,使得directive將更新後的值渲染到DOM中。express
controller和directive二者都與scope有引用,但它們二者之間沒有(引用)(Both controllers and directives have reference to the scope, but not to each other)。這樣的安排,將controller從directive和DOM中隔離開來。這是一個重要的地方,由於它讓controller與view是隔離的,極大地提高了應用的可測試性(greatly improves the testing story of the applications)。bootstrap
<!DOCTYPE HTML> <html lang="zh-cn" ng-app> <head> <meta charset="UTF-8"> <title>data-model</title> <style type="text/css"> .ng-cloak { display: none; } </style> </head> <body class="ng-cloak"> <div ng-controller="MyController"> 你的名字: <input type="text" ng-model="username"/> <button ng-click="sayHello()">歡迎</button> <hr/> {{greeting}} </div> <script src="../angular-1.0.1.js" type="text/javascript"></script> <script type="text/javascript"> function MyController($scope) { $scope.username = "My Little Dada"; $scope.sayHello = function() { $scope.greeting = "Hello~" + $scope.username + "!"; }; } </script> </body> </html>
在上面的例子中咱們能夠注意到MyController以」My Little Dada」對scope中的username屬性進行賦值。而後,scope通知input進行賦值,將username的值預先填入input中。這展現了controller如何作纔可以寫入數據到scope中。api
類似地,controller能夠將行爲附加在scope中,正如那個當用戶點擊「歡迎」按鈕時觸發的sayHello方法同樣。sayHello方法能夠讀取username屬性,也能夠建立greeting屬性。這代表,當它們綁定到HTML input控件時,scope中的屬性會自動更新。瀏覽器
邏輯上,顯示{{greeting}}涉及如下兩點:app
咱們能夠認爲,scope和它本身的屬性能夠做爲數據,用於渲染視圖。scope是全部和view相關的東西單一的真相來源(The scope is the single source-of-truth for all things view related)。
從可測試性來看,controller和view的分離是值得欣喜的,由於它容許咱們在沒有渲染細節的干擾下(專一於)測試行爲。
it('should say hello', function() { var scopeMock = {}; var cntl = new MyController(scopeMock); // Assert that username is pre-filled expect(scopeMock.username).toEqual('World'); // Assert that we read new username and greet scopeMock.username = 'angular'; scopeMock.sayHello(); expect(scopeMock.greeting).toEqual('Hello angular!'); });
4、Scope Hierarchies(scope層次結構)
每個angular應用有且只有一個root scope,但能夠擁有多個child scope。
應用能夠擁有多個child scope,由於一些directive會建立新的child scope(參考directive文檔,查看哪些directive可建立新的scope,如ng-repeat)。當新的scope被建立後,他們將做爲一個child scope,加入到parent scope中。這樣,建立了一個與它們附屬的DOM類似的樹結構。
當angular對{{username}}求值時,它首先查看與當前元素關聯的scope的username屬性。若是沒有找到對應的屬性,它將會一直向上搜索parent scope,直到到達root scope。在javascript中,這個行爲被稱爲「原型繼承」,child scope典型地繼承自它們的parent。
這個例子說明應用中的scope(是怎樣的),屬性的原型繼承。
<!DOCTYPE HTML> <html lang="zh-cn" ng-app> <head> <meta charset="UTF-8"> <title>scope-hierarchies</title> <style type="text/css"> .ng-cloak { display: none; } .ng-scope { border: 1px dashed red; } </style> </head> <body class="ng-cloak"> <div ng-controller="MyController"> 經理:{{employee.name}} [{{department}}] <br/> 報告: <ul> <li ng-repeat="employee in employee.reports"> {{employee.name}} [{{department}}] </li> </ul> <hr/> {{greeting}} </div> <script src="../angular-1.0.1.js" type="text/javascript"></script> <script type="text/javascript"> function MyController($scope) { $scope.department = "某部"; $scope.employee = { name:"My Little Dada", reports: [ {name:"Lcllao"}, {name:"那個誰^o^"} ] }; } </script> </body> </html>
注意,angular自動放置ng-scope class到與scope粘附的元素中。<style>定義在上面的例子中,經過紅色的虛線,高亮新的scope的範圍。由於repeater對{{employee.name}}表達式求值,child scope是必須的,但取決於表達式在哪個scope進行求值,不一樣的scope有不一樣的結果。類似地,{{department}}的值是從root scope中原型繼承得來的,只有在那個地方有,纔有department屬性的定義。
5、Retrieving Scopes from the DOM(從DOM中檢索scope)
scope做爲$scope數據屬性附加到DOM中,能夠被用於以調試做爲目的的檢索。(在應用中經過這個方式檢索Scope是不可能的。)附加到的DOM的root scope的位置是經過ng-app directive的位置定義的。一般ng-app是放置在<html>元素中,但它也能夠放置在其餘元素中,例如,只有一部分視圖須要被angular控制。
在debugger中查看scope:
1. 在瀏覽器中,對着感興趣的元素點擊右鍵,選擇「查看元素」。咱們能夠看到瀏覽器debugger高亮了咱們選中的元素。
2. debugger容許咱們在console中經過$0變量去訪問當前選擇的元素。
3. 想查看關聯的scope,咱們能夠在console中輸入:angular.element($0).scope()
6、Scope Events Propagation(Scope事件傳播)
scope能夠以相似於DOM事件的方式進行事件傳播。事件能夠被broadcast(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope#$broadcast)到child scope或者emit(http://code.angularjs.org/1.0.2/docs/api/ng.$rootScope.Scope#$emit)到parent scope中。(當前scope若是有監聽,也會執行)
<!DOCTYPE HTML> <html lang="zh-cn" ng-app> <head> <meta charset="UTF-8"> <title>scope-event-propagation</title> <style type="text/css"> .ng-cloak { display: none; } </style> </head> <body class="ng-cloak"> <div ng-controller="MyController"> root scope count:{{count}} <ul> <li ng-repeat="i in [1]" ng-controller="MyController"> <button ng-click="$emit('MyEvent')">$emit("MyEvent")</button> <button ng-click="$broadcast('MyEvent')">$broadcast("MyEvent")</button> <br/> middle scope count:{{count}} <ul> <li ng-repeat="item in [1,2]" ng-controller="MyController"> Leaf scope count:{{count}} </li> </ul> </li> </ul> </div> <script src="../angular-1.0.1.js" type="text/javascript"></script> <script type="text/javascript"> function MyController($scope) { $scope.count = 0; $scope.$on("MyEvent", function() { $scope.count++; }); } </script> </body> </html>
7、Scope Life Cycle(scope生命週期)
瀏覽器正常的事件流中,當瀏覽器接收到事件後,它會執行一個相應的javascript回調。一旦回調函數執行完畢後,瀏覽器將會重繪DOM,並返回到繼續等待事件的狀態。
當瀏覽器在angular執行環境外調用javascript代碼時,這意味着angular是不知道model的改變的。要正確處理model的修改,這個命令必須經過使$apply方法進入angular執行環境。只有在$apply方法中的model變動,纔會正確地被angular統計。例如,一個directive監聽了DOM事件,例如ng-click,它必須在$apply方法中對錶達式進行求值。
在對錶達式求值以後,$apply方法執行一個$digest。在$digest階段裏,scope檢查全部$watch監聽的表達式,將如今的值與舊的值做比較。髒檢查(dirty checking)是異步的。這意味着賦值語句(例如$scope.username=」angular」)將不會立刻致使一個$watch被通知,反而,$watch的通知將會延遲到$digest階段。這個延遲是必須的,由於它把多個model更新聯合到一個$watch通知中,這保證了在$watch通知的過程當中,沒有其餘$watch在執行。若是一個$watch改變了model的值,那麼它將會強制增長一個$digest週期。
1) Creation(建立scope)
root scope是在應用啓動的過程當中,被$injector(http://code.angularjs.org/1.0.2/docs/api/AUTO.$injector)建立的。在模版linking的過程當中,一些directive會建立新的child scope。
2) Watcher registration(註冊watcher)
在模版linking過程當中,directive在scope中註冊$watch。這些watch將會被用做向DOM傳播model的值。
3) Model mutation(Model變化)
爲了讓變化被正確地檢測,咱們須要將他們包裹在scope.$apply中。(angular API 已經隱式地作了這部操做,因此,當在controller中作同步的工做或者與$http或者$timeout一塊兒作異步工做的時候,不須要額外的$apply調用)。
4) Mutation observation(變化監測)
在$apply的結尾,angular會在root scope執行一個$digest週期,這將會傳播到全部child scope中。在$digest週期中,全部註冊了$watch的表達式或者function都會被檢查,判斷model是否發生了改變,若是改變發生了,那麼對應的$watch監聽器將會被調用。
5) Scope destruction(scope銷燬)
當child scope再也不是必須的時候,child scope的產生者有責任經過scope.$destroy() API銷燬它們(child scope)。這將會中止$digest的調用傳播傳播到child scope中,讓被child scope model使用的內存能夠被gc(garbage collector)回收。
1. Scopes and Directives
在編譯階段中,compiler依靠DOM模版匹配directive。directive一般能夠分爲兩大類:
當一個外部的事件(例如用戶動做、timer或者XHR)被監聽到,相關的expression必須經過$apply方法應用到scope中,讓全部監聽器可以正確地更新。
2. Directives that Create Scopes
在大多數的狀況中,directive和scope是相互影響的,但不會建立新的scope實例。然而,一些directive(例如ng-controller和ng-repeat)會建立新scope,附加child scope到對應的DOM元素中。咱們經過使用angular.element(aDomElement).scope()查看任意DOM元素的scope。
3. Controllers and Scopes
在如下的狀況中,scope與controller是相互影響的:
4. Scope $watch Performance Considerations(Scope $watch的性能考慮)
在angular中,爲了檢測屬性的變化而對scope進行髒檢測(Dirty checking),是一個廣泛的操做。爲此,這要求dirty checking函數必須是高效的。應當心dirty checking函數不要作任何DOM訪問操做,由於DOM訪問的速度比訪問javascript對象屬性的速度要慢好幾個數量級。