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>

 执行上述代码,结果如下:

二、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、如果我们赋值子作用域的属性时,依然不会启动原型链查找。

 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代码如下:

 代码稍长,请自行打开
因为每个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如下:

 代码稍长,请自行打开
执行上述代码,效果如下:

 

--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访问父作用域。

 EnglishExpression
Demo如下:

 代码稍长,请自行打开
执行上述代码,操作如下:

 

四、总结
有四种类型的子作用域:

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

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

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

4、要继承于父作用域,且与任何的隔离作用的指令为兄弟关系—directive(transclude: true).

时间: 2024-09-17 03:55:26

AngularJS作用域详解介绍的相关文章

AngularJS 作用域详解及示例代码_AngularJS

范围扮演其视图连接控制器的角色一个特殊的JavaScript对象.范围包含了模型数据.在控制器,模型数据通过$scope对象访问. <script> var mainApp = angular.module("mainApp", []); mainApp.controller("shapeController", function($scope) { $scope.message = "In shape controller"; $s

AngularJs 指令详解及示例代码_AngularJS

对于指令,可以把它简单的理解成在特定DOM元素上运行的函数,指令可以扩展这个元素的功能. 首先来看个完整的参数示例再来详细的介绍各个参数的作用及用法: angular.module('myApp', []) .directive('myDirective', function() { return { restrict: String, priority: Number, terminal: Boolean, template: String or Template Function: func

Python中的变量和作用域详解_python

作用域介绍 python中的作用域分4种情况: L:local,局部作用域,即函数中定义的变量: E:enclosing,嵌套的父级函数的局部作用域,即包含此函数的上级函数的局部作用域,但不是全局的: G:globa,全局变量,就是模块级别定义的变量: B:built-in,系统固定模块里面的变量,比如int, bytearray等. 搜索变量的优先级顺序依次是:作用域局部>外层作用域>当前模块中的全局>python内置作用域,也就是LEGB. x = int(2.9) # int bu

AngularJS 路由详解和简单实例_AngularJS

AngularJS 路由 本章节我们将为大家介绍 AngularJS 路由. AngularJS 路由允许我们通过不同的 URL 访问不同的内容. 通过 AngularJS 可以实现多视图的单页Web应用(single page web application,SPA). 通常我们的URL形式为 http://runoob.com/first/page,但在单页Web应用中 AngularJS 通过 # + 标记 实现,例如: http://runoob.com/#/first http://r

AngularJS 模块详解及简单实例_AngularJS

AngularJS 模块 模块定义了一个应用程序. 模块是应用程序中不同部分的容器. 模块是应用控制器的容器. 控制器通常属于一个模块. 创建模块 你可以通过 AngularJS 的 angular.module 函数来创建模块: <div ng-app="myApp">...</div> <script> var app = angular.module("myApp", []); </script> "m

AngularJs bootstrap详解及示例代码_AngularJS

AngularJs学习笔记系列第一篇,希望我可以坚持写下去.本文内容主要来自 http://docs.angularjs.org/guide/ 文档的内容,但也加入些许自己的理解与尝试结果. 一.总括 本文用于解释Angular初始化的过程,以及如何在你有需要的时候对Angular进行手工初始化. 二.Angular <script> 标签 本例用于展示如何通过推荐的路径整合Angular,实现自动初始化. <!doctype html> <html xmlns:ng=&qu

AngularJS指令详解及示例代码_AngularJS

AngularJS指令用于扩展HTML.这些都是先从ng- 前缀的特殊属性.我们将讨论以下指令: ng-app - 该指令启动一个AngularJS应用. ng-init - 该指令初始化应用程序数据. ng-model - 此指令定义的模型,该模型是变量在AngularJS使用. ng-repeat - 该指令将重复集合中的每个项目的HTML元素. ng-app指令 ng-app 指令启动一个AngularJS应用.它定义根元素.它会自动初始化或启动加载包含AngularJS应用程序的Web页

Angularjs CURD 详解及实例代码_AngularJS

Angularjs CURD 前言        基于一个手机端的项目使用了angularjs,硬着头皮去用,有很多的疑问还需要一一去验证,刚开始总是感觉找不到北,总是感觉有很多概念,而且似乎ng既夹杂MVC又夹杂MVVM的思想, 忙里偷闲敲了个简单的CURD demo. 当然顺着这个demo还可以延伸很多知识点,比如: 带分页查询.连接后台数据库.调用WebApi.分层使用Servcice.Factory. 效果图   <script type="text/javascript"

AngularJS控制器详解及示例代码_AngularJS

AngularJS应用主要依赖于控制器来控制数据在应用程序中的流动.控制器采用ng-controller指令定义.控制器是一个包含属性/属性和JavaScript对象的功能.每个控制器接受$scope参数指定应用程序/模块,由控制器控制. <div ng-app="" ng-controller="studentController"> ... </div> 在这里,我们已经声明采用ng-controller指令的控制器studentCont