1、概要 |
在AngularJS中,子做用域(child scope)基本上都要继承自父做用域(parent scope)。html
但,事无绝对,也有特例,那就是指令中scope设置项为对象时,即scope:{…},这将会让指令建立一个并不继承自父做用域的子做用域,咱们称之为隔离做用域(isolated scope)。angularjs
指令中的scope一共能够有三个值,下面咱们再来温习下:web
指令之scopechrome |
|
scope: false数组 |
默认值,指令不会新建一个做用域,使用父级做用域。app |
scope: trueide |
指令会建立一个新的子做用域,原型继承于父级做用域。spa |
scope: {…}prototype |
指令会新建一个隔离做用域,不会原型继承父做用域。3d |
那么,理解AngularJS中做用域继承有什么用呢?
缘由之一就是,有利于咱们使用“双向绑定”(也就是在form表单元素中绑定ng-model),例如,在初学AngularJS时,咱们常会遇到“双向绑定”不起做用的时候,以下:
<!DOCTYPE html> <head> <meta charset="utf-8"/> <script src="angular.js"></script> </head> <body ng-app="myApp"> parent:<input type="text" ng-model="name"/> <div ng-controller="TestCtrl"> child: <input type="text" ng-model="name"/> </div> <script> var app = angular.module('myApp', []); app.controller('TestCtrl', function(){}); </script> </body> </html>
执行上述代码,结果以下:
其实AngularJS的做用域继承与JavaScript的原型继承是同样的逻辑,固,若是想要上述代码实现双向绑定,咱们能够利用ng-model绑定对象属性,来达到目的,以下:
<!DOCTYPE html> <head> <meta charset="utf-8"/> <script src="angular.js"></script> </head> <body ng-app="myApp"> parent:<input type="text" ng-model="obj.name"/> <div ng-controller="TestCtrl"> child: <input type="text" ng-model="obj.name"/> </div> <script> var app = angular.module('myApp', []); app.run(function($rootScope){ $rootScope.obj = {}; }); app.controller('TestCtrl', function(){}); </script> </body> </html>
执行上述代码,结果以下:
该篇博客原文地址:http://www.cnblogs.com/giggle/p/5769047.html
2、JavaScript原型继承 |
上面已经提到了AngularJS的做用域继承与JavaScript的原型继承是同样儿同样儿的,因此,咱们首先来初步温习下JavaScript的原型继承。
假设,咱们有父做用域(ParentScope),且其中包含了属性aString、aNumber、anArray、anObject 以及aFunction。
好了,若是如今有一子做用域(ChildScope)继承于这个父做用域(ParentScope),以下所示:
当咱们经过ChildScope想访问一个属性时,JavaScript内部是如何为咱们查找的呢?
答案:
首先JavaScript会第一时间在当前做用域(如这里的ChildScope)中查找是否有这一属性,若是在当前做用域中没有找到,
那么JavaScript就会沿着原型这条链(如这里的:ChildScope-->ParentScope-->RootScope),一直找下去,假若在某一父做用域中找到,就返回这个属性值并中止原型链查找;
假若一直找到根做用域(如这里的RootScope)都没有找到,则返undefined。
故而,下面这些表达式,结果都为true:
childScope.aString === 'parent string' //true childScope.anArray[1] === 20 //true childScope.anObject.property1 === 'parent prop1' //true childScope.aFunction() === 'parent output' //true
好了,假如,咱们这么作呢:
childScope.aString = 'child string'
那么只会在childScope中新建一个值为’child string’的aString属性。在这以后,假若咱们还想经过childScope访问parentScope中aString属性时,就一筹莫展了。
由于childScope已经有了一个aString属性。理解这一点是很是重要的,在咱们讨论ng-repeat 和ng-include以前。
接下来,咱们再这么作呢:
childScope.anArray[1] = '22'
childScope.anObject.property1 = 'child prop1'
这样会沿着原型链查找的,并改变属性中的值。
为何呢?
缘由就是咱们此次赋值的是对象中的属性。
好了,接下来,咱们再这么作:
childScope.anArray = [100, 555]
childScope.anObject = {name: 'Mark', country: 'USA'}
这样作的效果,如同上面childScope.aString = ‘child string’同样,不会启动原型链查找。
总结:
一、若是咱们读取子做用域的属性时,且该子做用域有这个属性,则不会启动原型链查找;
二、若是咱们赋值子做用域的属性时,依然不会启动原型链查找。
一、If we read childScope.propertyX, and childScope has propertyX, then the prototype chain is not consulted.
二、If we set childScope.propertyX, the prototype chain is not consulted.
经过上面的总结,若是咱们想让childScope在已有本身的anArray属性后,仍然访问parentScope中的anArray值呢?
哈哈,删除childScope中的anArray属性嘛,以下:
delete childScope.anArray childScope.anArray[1] === 22 //true
3、Angular做用域继承 |
在前面“概要”部分已经说到Angular中子做用域基本上都继承自父做用域,但也有例外(scope:{…}指令),但并无具体说明哪些指令会建立子做用域等,如今归类以下:
建立子做用域,且继承自父做用域 |
一、 ng-repeat 二、 ng-include 三、 ng-switch 四、 ng-controller 五、 directive (scope: true) 六、 directive(transclude: true) |
建立子做用域,但并不继承自父做用域 |
directive(scope: {…}) |
不建立做用域 |
directive(scoep: false) |
下面分别看看:
--ng-include--
假设咱们有一控制器,内容以下:
module.controller('parentCtrl', function($scope){ $scope.myPrimitive = 50; $scope.myObject = {aNumber: 11}; });
有HTML代码以下:
<div ng-controller="parentCtrl"> parent-myPrimitive:<input type="text" ng-model="myPrimitive"/><br/> parent-obj.aNumber:<input type="text" ng-model="myObject.aNumber"/><br/> <script type="text/ng-template" id="/tpl1.html"> includ-myPrimitive:<input ng-model = "myPrimitive"/> </script> <div ng-include src="'/tpl1.html'"></div> <script type="text/ng-template" id="/tpl2.html"> includ-obj.aNumber:<input ng-model="myObject.aNumber"/> </script> <div ng-include src="'/tpl2.html'"></div> </div>
由于每一个ng-include指令,都会建立一个新的做用域,且继承于父做用域。固,代码中的关系图以下:
在chrome(需加--disable-web-security)下,执行上述代码后,得下:
从执行结果看,符合上面的关系图。
好了,假若咱们在第三个input框中,敲入字符(如77)后,子做用域会建立一个本身的myPrimitive属性,从而阻止原型链查找。
以下所示:
假若,咱们在第四个input框中,输入字符(如99)呢?子做用域会沿着原型链查找并修改,由于在这个input框中,咱们绑定的是对象属性。
以下图所示:
若是咱们想让第三个input框达到第四个input框的效果,而且不使用对象属性的方法,那么咱们能够利用$parent来达到目的。
修改第三个input框的HTML代码:
includ-myPrimitive:<input ng-model="$parent.myPrimitive"/>
好了,这个时候,咱们再在第三个input框中输入22时,就会沿着原型链查找了,达到与第四个input框同样的效果(由于$parent是为了让子做用域访问父做用域设置的属性)。
除开$parent,还有$$childHead和$$childTail都是为了子做用和父做用域通讯服务的。注意,是全部子做用域哦,因此包括了scope:{…}指令的状况。
--ng-switch--
ng-switch会建立子做用域,效果与上面所述的ng-include同样。假若,咱们想要实现子做用域与父做用域实现双向绑定,就使用$parent或者对象属性。
Demo以下:
<!DOCTYPE html> <head> <meta charset="utf-8"/> <script src="angular.js"></script> </head> <body ng-app="myModule"> <div ng-controller="parentCtrl"> <input type="text" ng-model="obj.something"/> <div ng-switch="name"> <div ng-switch-when="Monkey"> <h1>This is Monkey</h1> <input type="text" ng-model="obj.something"/> </div> <div ng-switch-default> <h1>This is Default</h1> </div> </div> </div> <script> var module = angular.module('myModule', []); module.controller('parentCtrl', function($scope){ $scope.obj = {}; $scope.name = "Monkey"; }); </script> </body> </html>
执行上述代码,效果以下:
--ng-repeat--
ng-repeat也会建立子做用域,不过与上面讲述的ng-include、ng-switch不一样的是,ng-repeat会为每一个遍历的元素建立子做用域,且继承自同一父做用域。
好了,下面咱们来具体看看,假如,如今咱们有一控制器,以下:
module.controller('parentCtrl', function($scope){ $scope.myArrayOfPrimitives = [11, 22]; $scope.myArrayOfObjects = [{num: 101}, {num: 202}]; });
HTML代码以下:
<div ng-controller="parentCtrl"> <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> </div>
由于,ng-repeat会为每一个元素都建立一个子做用域,如上面代码中<li ng-repeat=”num in myArrayOfPrimitives”>,ng-repeat指令会用num,去遍历myArrayOfPrimitives:[11, 22]中的数据,即建立两个继承自父做用域的子做用域。且,在子做用域中会建立本身的属性num,所以,假若子做用域中的num值变更,确定不会改变父做用域中myArrayOfPrimitives的相应数据咯。
然而,HTML代码中第二个出现ng-repeat的地方<li ng-repeat=”obj in myArrayOfObjects”>,当子做用域变更时,会影响到父做用域中对应的元素。由于数组myArrayOfObjects:[{…}, {…}]中的元素为对象,固而,遍历myArrayOfObjects中元素时,子做用域赋值的是对象,引用类型嘛,因此子做用域中变更对象属性时,确定会影响父做用域的相关值咯。
--ng-controller--
ng-controller指令与ng-include、ng-switch同样,即建立子做用域,且继承自父做用域。
--directives--
详情见“初探指令”
须要注意的是,scope为对象的指令(scope:{…}),虽然该类指令的做用域为隔离做用域,可是,它任然能够经过$parent访问父做用域。
Isolate scope's __proto__ references Object. Isolate scope's $parent references the parent scope, so although it is isolated and doesn't inherit prototypically from the parent scope, it is still a child scope.
Demo以下:
<!DOCTYPE html> <head> <meta charset="utf-8"/> <script src="angular.js"></script> </head> <body ng-app="myApp"> <div ng-controller="TestCtrl"> <input type="text" ng-model="name"/> <test></test> </div> <script> var app = angular.module('myApp', []); app.controller('TestCtrl', function($scope){ $scope.name = 'Monkey'; }); app.directive('test', function(){ return { restrict: 'E', scope: {}, controller: function($scope){ $scope.name = $scope.$parent.name; }, template: '<input type="text" ng-model="$parent.name"/>' }; }); </script> </body> </html>
执行上述代码,操做以下:
4、总结 |
有四种类型的子做用域:
一、要继承于父做用域—ng-include, ng-switch, ng-controller, directive(scope: true).
二、要继承于父做用域,但会为每一个元素建立一个子做用域—ng-repeat.
三、隔离做用域—directive(scope:{…}).
四、要继承于父做用域,且与任何的隔离做用的指令为兄弟关系—directive(transclude: true).
the directive creates a new "transcluded" child scope, which prototypically inherits from the parent scope. The transcluded and the isolated scope (if any) are siblings -- the $parent property of each scope references the same parent scope. When a transcluded and an isolate scope both exist, isolate scope property $$nextSibling will reference the transcluded scope.
--Summary_in_English--
There are four types of scopes:
一、normal prototypal scope inheritance -- ng-include, ng-switch, ng-controller, directive with scope: true
二、normal prototypal scope inheritance with a copy/assignment -- ng-repeat. Each iteration of ng-repeat creates a new child scope, and that new child scope always gets a new property.
三、isolate scope -- directive with scope: {...}. This one is not prototypal, but '=', '@', and '&' provide a mechanism to access parent scope properties, via attributes.
四、transcluded scope -- directive with transclude: true. This one is also normal prototypal scope inheritance, but it is also a sibling of any isolate scope.
For all scopes (prototypal or not), Angular always tracks a parent-child relationship (i.e., a hierarchy), via properties $parent and $$childHead and $$childTail.
5、参考 |