<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                # 理解AngularJS的作用域Scope *2013年8月2日, 嚴清 譯* [TOC] ## 概敘: AngularJS中,子作用域一般都會通過JavaScript原型繼承機制繼承其父作用域的屬性和方法。但有一個例外:在directive中使用scope: { ... },這種方式創建的作用域是一個獨立的"Isolate"作用域,它也有父作用域,但父作用域不在其原型鏈上,不會對父作用域進行原型繼承。這種方式定義作用域通常用于構造可復用的directive組件。 作用域的原型繼承是非常簡單普遍的,甚至你不必關心它的運作。直到你在子作用域中向父作用域的原始類型屬性使用雙向數據綁定2-way data binding,比如Form表單的ng-model為父作用域中的屬性,且為原始類型,輸入數據后,它不會如你期望的那樣運行——AngularJS不會把輸入數據寫到你期望的父作用域屬性中去,而是直接在子作用域創建同名屬性并寫入數據。這個行為符合JavaScript原型繼承機制的行為。AngularJS新手通常沒有認識到ng-repeat、 ng-switch、ng-view和ng-include 都會創建子作用域, 所以經常出問題。 (見 [示例](http://plnkr.co/edit/zZfUQN?p=preview)) 避免這個問題的最佳實踐是在ng-model中總使用.,參見文章 [always have a '.' in your ng-models。](http://www.youtube.com/watch?v=ZhfUv0spHCY&feature=youtu.be&t=30m) 比如: `<input type="text" ng-model="someObj.prop1">` 優于: `<input type="text" ng-model="prop1">` 如果你一定要直接使用原始類型,要注意兩點: 在子作用域中使用 $parent.parentScopeProperty,這樣可以直接修改父作用域的屬性。 在父作用域中定義函數,子作用域通過原型繼承調用函數把值傳遞給父作用域(這種方式極少使用)。 ## JavaScript 原型繼承機制 你必須完全理解JavaScript的原型繼承機制,尤其是當你有后端開發背景和類繼承經驗的時候。所以我們先來回顧一下原型繼承: 假設父作用域parentScope擁有以下屬性和方法:aString、aNumber、anArray、anObject、aFunction。子作用域childScope如果從父作用域parentScope進行原型繼承,我們將看到: ![](https://box.kancloud.cn/69b7566770f3943bd54450c291701cbd_619x257.png) *`normal prototypal inheritance`* (**注**:為節約空間,anArray使用了藍色方塊圖) 如果我們在子作用域中訪問一個父作用域中定義的屬性,JavaScript首先在子作用域中尋找該屬性,沒找到再從原型鏈上的父作用域中尋找,如果還沒找到會再往上一級原型鏈的父作用域尋找。在AngularJS中,作用域原型鏈的頂端是$rootScope,JavaScript尋找到$rootScope為止。所以,以下表達式均為true: ~~~ childScope.aString === 'parent string' childScope.anArray[1] === 20 childScope.anObject.property1 === 'parent prop1' childScope.aFunction() === 'parent output' ~~~ 如果我們進行如下操作: `childScope.aString = 'child string'` 因為我們賦值目標是子作用域的屬性,原型鏈將不會被查詢,一個新的與父作用域中屬性同名的屬性`aString`將被添加到當前的子作用域`childScope`中。 ![](https://box.kancloud.cn/a0b23356fdd82cc146e9487389c71e9e_619x257.png) *`shadowing`* 如果我們進行如下操作: ~~~ childScope.anArray[1] = '22' childScope.anObject.property1 = 'child prop1' ~~~ 因為我們的賦值目標是子作用域屬性anArray和anObject的子屬性,也就是說JavaScript必須先要先尋找anArray和anObject這兩個對象——它們必須為對象,否則不能寫入屬性,而這兩個對象不在當前子作用域,原型鏈將被查詢,在父作用域中找到這兩個對象, 然后對這兩個對象的屬性[1]和property1進行賦值操作。子作用域中不會不會創建兩個新的同名屬性!(注意JavaScript中數組和函數均是對象——引用類型) ![](https://box.kancloud.cn/ae3d7a24951cfd8fcc15f0f3695818aa_608x257.png) *`follow the chain`* 如果我們進行如下操作: ~~~ childScope.anArray = [100, 555] childScope.anObject = { name: 'Mark', country: 'USA' } ~~~ 同樣因為我們賦值目標是子作用域的屬性,原型鏈將不會被查詢,,JavaScript會直接在子作用域創建兩個同名屬性,其值分別為數組和對象。 ![](https://box.kancloud.cn/56f7a9ad6c2836d5c2ad165de173eda4_608x320.png) *`not following the chain`* 要點: * 如果我們讀取`childScope.propertyX`,并且`childScope`存在`propertyX`,原型鏈不會被查詢; * 如果我們寫入`childScope.propertyX`, 原型鏈也不會被查詢; * 如果我們寫入`childScope.propertyX.subPropertyY,` 并且`childScope`不存在`propertyX`,原型鏈將被查詢——查找propertyX。 最后一點: ~~~ delete childScope.anArray childScope.anArray[1] === 22 // true ~~~ 如果我們先刪除了子作用域childScope的屬性,然后再讀取該屬性,因為找不到該屬性,原型鏈將被查詢。 ![](https://box.kancloud.cn/1b871381c230db81df68071fecbffc0b_608x320.png) *`after deleting a property`* ## AngularJS 作用域Scope的繼承 提示: **以下方式會創建新的子作用域,并且進行原型繼承: ng-repeat、ng-include、ng-switch、ng-view、ng-controller, 用scope: true和transclude: true創建directive。 以下方式會創建新的獨立作用域,不會進行原型繼承:用scope: { ... }創建directive。這樣創建的作用域被稱為"Isolate"作用域**。 **注意**:默認情況下創建directive使用了scope: false,不會創建子作用域。 進行原型繼承即意味著父作用域在子作用域的原型鏈上,這是JavaScript的特性。AngularJS的作用域還存在如下內部定義的關系: * scope.$parent指向scope的父作用域; * scope.$$childHead指向scope的第一個子作用域; * scope.$$childTail指向scope的最后一個子作用域; * scope.$$nextSibling指向scope的下一個相鄰作用域; * scope.$$prevSibling指向scope的上一個相鄰作用域; 這些關系用于AngularJS內部歷遍,如$broadcast和$emit事件廣播,$digest處理等。 ### **ng-include** In controller: ~~~ $scope.myPrimitive = 50; $scope.myObject = {aNumber: 11}; ~~~ In HTML: ~~~ <script type="text/ng-template" id="/tpl1.html"> <input ng-model="myPrimitive"> </script> <div ng-include src="'/tpl1.html'"></div> <script type="text/ng-template" id="/tpl2.html"> <input ng-model="myObject.aNumber"> </script> <div ng-include src="'/tpl2.html'"></div> ~~~ 每一個ng-include指令都創建一個子作用域, 并且會從父作用域進行原型繼承。 ![](https://box.kancloud.cn/653f916953f8d7d5cd3caf2adfcf9e37_541x115.png) *`ng-include`* 在第一個input框輸入"77"將會導致子作用域中新建一個同名屬性,其值為77,這不是你想要的結果。 ![](https://box.kancloud.cn/11e302e8d79fd2471175c76b648d33fd_541x124.png) *`ng-include primitive`* 在第二個input框輸入"99"會直接修改父作用域的myObject對象,這就是JavaScript原型繼承機制的作用。 ![](https://box.kancloud.cn/e95bc0e1300f7e8fc840e877e2636409_541x124.png) *`ng-include object`* (**注**:上圖存在錯誤,紅色99因為是50,11應該是99) 如果我們不想把model由原始類型改成引用類型——對象,我們也可以使用$parent直接操作父作用域: `<input ng-model="$parent.myPrimitive">` 輸入"22"我們得到了想要的結果。 ![](https://box.kancloud.cn/20cf057b2f213d790048813bf1a26e14_541x117.png) *`ng-include $parent`* 另一種方法就是使用函數,在父作用域定義函數,子作用域通過原型繼承可運行該函數: ~~~ // in the parent scope $scope.setMyPrimitive = function(value) { $scope.myPrimitive = value; } ~~~ 請參考: [sample fiddle](http://jsfiddle.net/mrajcok/jNxyE/) that uses this "parent function" approach. (This was part of a [Stack Overflow post](http://stackoverflow.com/a/14104318/215945).) http://stackoverflow.com/a/13782671/215945 https://github.com/angular/angular.js/issues/1267. ### **ng-switch** ng-switch與ng-include一樣。 參考:[ AngularJS, bind scope of a switch-case?](http://stackoverflow.com/questions/12405005/angularjs-bind-scope-of-a-switch-case/12414410) ### ng-view ng-view與ng-include一樣。 ### ng-repeat Ng-repeat也創建子作用域,但有些不同。 In controller: ~~~ $scope.myArrayOfPrimitives = [ 11, 22 ]; $scope.myArrayOfObjects = [{num: 101}, {num: 202}] ~~~ In HTML: ~~~ <ul><li ng-repeat="num in myArrayOfPrimitives"> <input ng-model="num"> </li> </ul> <ul><li ng-repeat="obj in myArrayOfObjects"> <input ng-model="obj.num"> </li> </ul> ~~~ ng-repeat對每一個迭代項Item都會創建子作用域, 子作用域也從父作用域進行原型繼承。 **但它還是會在子作用域中新建同名屬性,把Item賦值給對應的子作用域的同名屬性。** 下面是AngularJS中ng-repeat的部分源代碼: ~~~ childScope = scope.$new(); // child scope prototypically inherits from parent scope ... childScope[valueIdent] = value; // creates a new childScope property ~~~ 如果Item是原始類型(如myArrayOfPrimitives的11、22), 那么子作用域中有一個新屬性(如num),它是Item的副本(11、22). 修改子作用域num的值將**不會**改變父作用域myArrayOfPrimitives,所以在上一個ng-repeat,每一個子作用域都有一個num 屬性,該屬性與myArrayOfPrimitives無關聯: ![](https://box.kancloud.cn/2becd3389732b4e284500fdfcb820f15_440x153.png) *`ng-repeat primitive`* 顯然這不會是你想要的結果。我們需要的是在子作用域中修改了值后反映到myArrayOfPrimitives數組。我們需要使用引用類型的Item,如上面第二個ng-repeat所示。 myArrayOfObjects的每一項Item都是一個對象——引用類型,ng-repeat對每一個Item創建子作用域,并在子作用域新建obj屬性,obj屬性就是該Item的一個引用,而不是副本。 ![](https://box.kancloud.cn/14a076cab60c32b84eba8d2b58c40084_560x152.png) *`ng-repeat object`* 我們修改子作用域的obj.num就是修改了myArrayOfObjects。這才是我們想要的結果。 參考: [Difficulty with ng-model, ng-repeat, and inputs](http://stackoverflow.com/questions/13714884/difficulty-with-ng-model-ng-repeat-and-inputs) [ng-repeat and databinding](http://stackoverflow.com/a/13782671/215945) ### ng-controller 使用ng-controller與ng-include一樣也是創建子作用域,會從父級controller創建的作用域進行原型繼承。但是,利用原型繼承來使父子controller共享數據是一個糟糕的辦法。[ "it is considered bad form for two controllers to share information via $scope inheritance"](http://onehungrymind.com/angularjs-sticky-notes-pt-1-architecture/),controllers之間應該使用 service進行數據共享。 (如果一定要利用原型繼承來進行父子controllers之間數據共享,也可以直接使用。 請參考:[Controller load order differs when loading or navigating](http://stackoverflow.com/questions/13825419/controller-load-order-differs-when-loading-or-navigating/13843771#13843771)) ## directives 1. 默認 (`scope: false`) - directive使用原有作用域,所以也不存在原型繼承,這種方式很簡單,但也很容易出問題——除非該directive與html不存在數據綁定,否則一般情況建議使用第2條方式。 2. scope: true - directive創建一個子作用域, 并且會從父作用域進行原型繼承。 如果同一個DOM element存在多個directives要求創建子作用域,那么只有一個子作用域被創建,directives共用該子作用域。 3. scope: { ... } - directive創建一個獨立的“Isolate”作用域,沒有原型繼承。這是創建可復用directive組件的最佳選擇。因為它不會直接訪問/修改父作用域的屬性,不會產生意外的副作用。這種directive與父作用域進行數據通信有如下四種方式(更詳細的內容請參考[Developer Guide](http://docs.angularjs.org/guide/directive)): 1. = or =attr “Isolate”作用域的屬性與父作用域的屬性進行雙向綁定,任何一方的修改均影響到對方,這是最常用的方式; 2. @ or @attr “Isolate”作用域的屬性與父作用域的屬性進行單向綁定,即“Isolate”作用域只能讀取父作用域的值,并且該值永遠的String類型; 3. & or &attr “Isolate”作用域把父作用域的屬性包裝成一個函數,從而以函數的方式讀寫父作用域的屬性,包裝方法是$parse,詳情請見[API-$parse](http://docs.angularjs.org/api/ng.$parse); “Isolate”作用域的__proto__是一個標準[Scope](http://docs.angularjs.org/api/ng.$rootScope.Scope) object (the picture below needs to be updated to show an orange 'Scope' object instead of an 'Object'). “Isolate”作用域的$parent同樣指向父作用域。它雖然沒有原型繼承,但它仍然是一個子作用域。 如下directive: ` <my-directive interpolated="{{parentProp1}}" twowayBinding="parentProp2"> ` scope: `scope: { interpolatedProp: '@interpolated', twowayBindingProp: '=twowayBinding' }` link函數中: ` scope.someIsolateProp = "I'm isolated"` ![](https://box.kancloud.cn/5cd9658d13d89adf4a0c36a1170216d0_467x157.png) *`isolate scope`* 請注意,我們在link函數中使用`attrs.$observe('interpolated', function(value) { ... }`來監測`@`屬性的變化。 更多請參考: http://onehungrymind.com/angularjs-sticky-notes-pt-2-isolated-scope/ 4. `transclude: true` - directive新建一個“transcluded”子作用域,并且會從父作用域進行原型繼承。需要注意的是,“transcluded”作用域與“Isolate”作用域是相鄰的關系(如果“Isolate”作用域存在的話) -- 他們的$parent屬性指向同一個父作用域。“Isolate”作用域的$$nextSibling指向“transcluded”作用域。 更多請參考: [AngularJS two way binding not working in directive with transcluded scope](http://stackoverflow.com/a/14484903/215945) ![](https://box.kancloud.cn/854490c9028b120d7637d159bc2be754_640x226.png) *`transcluded scope`* demo: [fiddle](http://jsfiddle.net/mrajcok/7g3QM/) # **總結** AngularJS存在四種作用域: 1. 普通的帶原型繼承的作用域 -- ng-include, ng-switch, ng-controller, directive with scope: true; 2. 普通的帶原型繼承的,并且有賦值行為的作用域 -- ng-repeat,ng-repeat為每一個迭代項創建一個普通的有原型繼承的子作用域,但同時在子作用域中創建新屬性存儲迭代項; 3. “Isolate”作用域 -- directive with scope: {...}, 該作用域沒有原型繼承,但可以通過'=', '@', 和 '&'與父作用域通信。 4. “transcluded”作用域 -- directive with transclude: true,它也是普通的帶原型繼承的作用域,但它與“Isolate”作用域是相鄰的好基友。 Diagrams were generated with [GraphViz ](http://graphviz.org/)"*.dot" files, which are on [github](https://github.com/mrajcok/angularjs-prototypal-inheritance-diagrams). The above was originally posted on [StackOverflow](http://stackoverflow.com/questions/14049480/what-are-the-nuances-of-scope-prototypal-prototypical-inheritance-in-angularjs).
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看