一、概要

在AngularJS中,子作用域(child scope)基本上都要继承自父作用域(parent scope)。

但,事无绝对,也有特例,那就是指令中scope设置项为对象时,即scope:{…},这将会让指令创建一个并不继承自父作用域的子作用域,我们称之为隔离作用域(isolated scope)。

指令中的scope一共可以有三个值,下面我们再来温习下:

指令之scope

scope: false

默认值,指令不会新建一个作用域,使用父级作用域。

scope: true

指令会创建一个新的子作用域,原型继承于父级作用域。

scope: {…}

指令会新建一个隔离作用域,不会原型继承父作用域。

那么,理解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

二、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’一样,不会启动原型链查找。

总结:

1、如果我们读取子作用域的属性时,且该子作用域有这个属性,则不会启动原型链查找;

2、如果我们赋值子作用域的属性时,依然不会启动原型链查找。

1、If we read childScope.propertyX, and childScope has propertyX, then the prototype chain is not consulted.
2、If we set childScope.propertyX, the prototype chain is not consulted.

EnglishExpression

通过上面的总结,如果我们想让childScope在已有自己的anArray属性后,仍然访问parentScope中的anArray值呢?

哈哈,删除childScope中的anArray属性嘛,如下:

delete childScope.anArray

childScope.anArray[1] === 22 //true

三、Angular作用域继承

在前面“概要”部分已经说到Angular中子作用域基本上都继承自父作用域,但也有例外(scope:{…}指令),但并没有具体说明哪些指令会创建子作用域等,现在归类如下:

创建子作用域,且继承自父作用域

1、  ng-repeat

2、  ng-include

3、  ng-switch

4、  ng-controller

5、  directive (scope: true)

6、  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. 

EnglishExpression

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>

代码稍长,请自行打开

执行上述代码,操作如下:

四、总结

有四种类型的子作用域:

1、要继承于父作用域—ng-include, ng-switch, ng-controller, directive(scope: true).

2、要继承于父作用域,但会为每个元素创建一个子作用域—ng-repeat.

3、隔离作用域—directive(scope:{…}).

4、要继承于父作用域,且与任何的隔离作用的指令为兄弟关系—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. 

transclude:true

--Summary_in_English--

There are four types of scopes:

1、normal prototypal scope inheritance -- ng-include, ng-switch, ng-controller, directive with scope: true
2、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.
3、isolate scope -- directive with scope: {...}. This one is not prototypal, but '=', '@', and '&' provide a mechanism to access parent scope properties, via attributes.
4、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.

Summary

五、参考

Understanding Scope

 

最新文章

  1. ubuntu下配置vpn
  2. 1472. Martian Army
  3. 为什么国外程序员爱用Mac?
  4. 【原】Storm Tutorial
  5. ArrayList 、Vector、 LinkList
  6. WebBrowser.ExecWB方法
  7. BaseFragment的定义—所有Fragment的父类
  8. 使用高性能xml序列化框架jibx作为spring mvc的xml view
  9. Python基础之 urllib模块urlopen()与urlretrieve()的使用方法详解。
  10. address_space 从哪里来
  11. 目录树生成工具treer
  12. nginx 禁止未绑定的域名访问
  13. 下面哪项技术可以用在WEB开发中实现会话跟踪实现?
  14. Vue--组件嵌套
  15. 数据结构--图 的JAVA实现(上)
  16. ie8 报错:意外地调用了方法或属性访问
  17. windows清除访问共享文件夹的登陆信息
  18. Sublimetext3将空格转换为Tab
  19. docker挂载本地目录和数据卷容器
  20. WPF性能调试系列 – Ants Performance Profiler

热门文章

  1. 【.net 深呼吸】细说CodeDom(1):结构大观
  2. 微软.NET Core RC2正式发布,横跨所有平台
  3. 【.net 深呼吸】跨应用程序域执行程序集
  4. 使用Java原生代理实现AOP
  5. SQL Server-聚焦查询计划Stream Aggregate VS Hash Match Aggregate(二十)
  6. c#多线程
  7. javascript 判断参数类型大全
  8. 《JavaScript设计模式与开发实践》整理
  9. ViewController respondsToSelector 错误的解决方法
  10. ECharts数据图表系统? 5分钟上手!