剖析AngularJS作用域
| 一、概要 |
在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
| 五、参考 |
剖析AngularJS作用域的更多相关文章
- AngularJS开发指南9:AngularJS作用域的详解
AngularJS作用域是一个指向应用模型的对象.它是表达式的执行环境.作用域有层次结构,这个层次和相应的DOM几乎是一样的.作用域能监控表达式和传递事件. 作用域的特点 作用域提供APIs($wat ...
- AngularJS 作用域(Scope)
AngularJS作用域(Scope) Scope作用域是应用在视图和控制器之间的纽带,Scope是一个对象包含可用的方法和属性,Scope可以应用在试图和控制器上. $scope是针对当前的cont ...
- AngularJS 作用域与数据绑定机制
AngularJS 简介 AngularJS 是由 Google 发起的一款开源的前端 MVC 脚本框架,既适合做普通 WEB 应用也可以做 SPA(单页面应用,所有的用户操作都在一个页面中完成).与 ...
- angularjs作用域
作用域(scope)①是构成AngularJS应用的核心基础,在整个框架中都被广泛使用,因此了解它如何工作是非常重要的.应用的作用域是和应用的数据模型相关联的,同时作用域也是表达式执行的上下文.$sc ...
- angularjs作用域之transclude
transclude是一个可选的参数.如果设置了,其值必须为true,它的默认值是false.嵌入有时被认为是一个高级主题,但某些情况下它与我们刚刚学习过的作用域之间会有非常好的配合.使用嵌入也会很好 ...
- 构建自己的AngularJS - 作用域和Digest(三)
作用域 第一章 作用域和Digest(三) $eval - 在当前作用域的上下文中运行代码 Angular有多种方式让你在当前作用域的上下文中运行代码.最简单的是$eval.传入一个函数当做其參数.然 ...
- angularjs作用域和函数调用
<!DOCTYPE HTML> <html ng-app> <head> <meta http-equiv="Content-Type" ...
- 构建自己的AngularJS - 作用域和Digest(一)
作用域 第一章 作用域和Digest(一) Angular作用域是简单javascript对象,因此你能够像对其它对象一样加入属性.然而,他们也有一些额外的功能:用于观測数据结构的变化.这样的观察能力 ...
- angularjs 作用域
1.指令属性取值:通过attr.someAttribute属性名字获取 以下,通过$eval(attr.data)获取value <div ng-controller="personC ...
随机推荐
- .NetCore中的日志(1)日志组件解析
.NetCore中的日志(1)日志组件解析 0x00 问题的产生 日志记录功能在开发中很常用,可以记录程序运行的细节,也可以记录用户的行为.在之前开发时我一般都是用自己写的小工具来记录日志,输出目标包 ...
- 从Membership 到 .NET4.5 之 ASP.NET Identity
我们前面已经讨论过了如何在一个网站中集成最基本的Membership功能,然后深入学习了Membership的架构设计.正所谓从实践从来,到实践从去,在我们把Membership的结构吃透之后,我们要 ...
- 23种设计模式--中介者模式-Mediator Pattern
一.中介者模式的介绍 中介者模式第一下想到的就是中介,房子中介,婚姻中介啊等等,当然笔者也希望来个婚姻中介给我介绍一个哈哈哈,,回归正题中介者模式分成中介者类和用户类,根据接口编程的方式我们再 ...
- load和initialize方法
一.load 方法什么时候调用: 在main方法还没执行的时候 就会 加载所有类,调用所有类的load方法. load方法是线程安全的,它使用了锁,我们应该避免线程阻塞在load方法. 在项目中使 ...
- SQL Server-聚焦计算列或计算列持久化查询性能(二十二)
前言 上一节我们详细讲解了计算列以及计算列持久化的问题,本节我们依然如前面讲解来看看二者查询性能问题,简短的内容,深入的理解,Always to review the basics. 持久化计算列比非 ...
- MFC中如何画带实心箭头的直线
工作中遇到话流程图的项目,需要画带箭头的直线,经过摸索,解决:思路如下: (1) 两个点(p1,p2)确定一个直线,以直线的一个端点(假设p2)为原点,设定一个角度 (2)以P2为原点得到向量P2P1 ...
- JQuery 选择器
选择器是JQuery的根基,在JQuery中,对事件的处理,遍历DOM和AJAX操作都依赖于选择器.如果能够熟练地使用选择器,不仅能简化代码,而且还可以事半功倍. JQuery选择器的优势 1.简洁的 ...
- 【SAP业务模式】之ICS(四):组织单元的配置
SAP的ICS业务后台配置主要有以下几个配置点: 1.组织单元的配置(公司代码.销售组织.工厂.采购组织等): 2.主数据的部分: 3.订单和开票的定价过程: 4.开票输出类型: 5.公司间发票的配置 ...
- Android中的多线程断点下载
首先来看一下多线程下载的原理.多线程下载就是将同一个网络上的原始文件根据线程个数分成均等份,然后每个单独的线程下载对应的一部分,然后再将下载好的文件按照原始文件的顺序"拼接"起来就 ...
- Form 表单提交参数
今天因为要额外提交参数数组性的参数给form传到后台而苦恼了半天,结果发现,只需要在form表单对应的字段html空间中定义name = 后台参数名 的属性就ok了. 后台本来是只有模型参数的,但是后 ...