AngularJS作用域

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).

 transclude:true

--Summary_in_English--

 Summary

五、参考

Understanding Scope

时间: 2024-11-08 18:52:07

AngularJS作用域的相关文章

angularJS 作用域

<!doctype html><html ng-app="firstApp"><head> <meta charset="utf-8"> <script src="angular-1.3.0.js"></script></head><body><pre> </pre><div ng-controller="par

AngularJS 作用域与数据绑定机制

AngularJS 简介 AngularJS 是由 Google 发起的一款开源的前端 MVC 脚本框架,既适合做普通 WEB 应用也可以做 SPA(单页面应用,所有的用户操作都在一个页面中完成).与同为 MVC 框架的 Dojo 的定位不同,AngularJS 在功能上更加轻量,而相比于 jQuery,AngularJS 又帮您省去了许多机械的绑定工作.在一些对开发速度要求高,功能模块不需要太丰富的非企业级 WEB 应用上,AngularJS 是一个非常好的选择.AngularJS 最为复杂同

剖析AngularJS作用域

一.概要 在AngularJS中,子作用域(child scope)基本上都要继承自父作用域(parent scope). 但,事无绝对,也有特例,那就是指令中scope设置项为对象时,即scope:{…},这将会让指令创建一个并不继承自父作用域的子作用域,我们称之为隔离作用域(isolated scope). 指令中的scope一共可以有三个值,下面我们再来温习下: 指令之scope scope: false 默认值,指令不会新建一个作用域,使用父级作用域. scope: true 指令会创建

angularjs作用域之transclude

transclude是一个可选的参数.如果设置了,其值必须为true,它的默认值是false.嵌入有时被认为是一个高级主题,但某些情况下它与我们刚刚学习过的作用域之间会有非常好的配合.使用嵌入也会很好地扩充我们的工具集,特别是在创建可以在团队.项目.AngularJS社区之间共享的HTML代码片段时. 嵌入通常用来创建可复用的组件,典型的例子是模态对话框或导航栏.我们可以将整个模板,包括其中的指令通过嵌入全部传入一个指令中.这样做可以将任意内容和作用域传递给指令.transclude参数就是用来

AngularJS 作用域里的值复制和引用复制

对象要么是值复制要么是引用复制.字符串.数字和布尔型变量是值 复制.数组.对象和函数则是引用复制. 1.值复制: <!doctype html> <html ng-app="myApp"> <head> <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.0-rc.3/angular.js"></script> </head

构建自己的AngularJS - 作用域和Digest(一)

作用域 第一章 作用域和Digest(一) Angular作用域是简单javascript对象,因此你可以像对其他对象一样添加属性.然而,他们也有一些额外的功能,用于观测数据结构的变化.这种观察能力是使用脏值检查在digest循环中运行来实现的.这就是我们这一章将要实现的内容. 作用域对象 Scope的创建是通过在Scope构造函数之前加入new关键字来创建的.这样会产生一个简单javascript对象.让我们先来创建一个单元测试.(测试驱动开发,先写测试案例) 对Scope创建一个测试文件te

构建自己的AngularJS - 作用域和Digest(二)

作用域 第一章 作用域和Digest(二) 放弃一个不稳定的Digest 在我们当前的实现中有一个明显的遗漏:如果发生了两个监控函数互相监控对方的变化的情况会如何?也就是,万一状态永远不能稳定呢?就像下面的测试案例展示的情况: test/scope_spec.js it("gives up on the watchers after 10 iterations", function(){ scope.counterA = 0; scope.counterB = 0; scope.$wa

创建自己的AngularJS - 作用域和Digest(五)

作用域 第一章 作用域和Digest(五) 销毁监控 当你注册一个监控,很多时候你想让它和scope一样保持活跃的状态,所以不必显示的删除他.然而,有些情况下,你需要销毁一个特定的监控,但是仍然保持作用域可操作.意思就是,我们需要给监控增加一个删除操作. Angular实现这个的方式特别聪明:Angular中的$watch函数有一个返回值.他是一个函数,但其被调用的时候,即删除了其注册的监控.如果想要能够移除一个监控,只要存储注册监控函数时返回的函数,然后当不需要监控的时候调用它即可: test

构建自己的AngularJS - 作用域和Digest(三)

作用域 第一章 作用域和Digest(三) $eval - 在当前作用域的上下文中执行代码 Angular有多种方式让你在当前作用域的上下文中执行代码.最简单的是$eval.传入一个函数当做其参数,然后将当前的作用域作为参数传给该函数,并执行它.然后它返回该函数的执行结果.$eval还有第二个可选的参数,它仅仅是被传递给将要执行的函数. 有几个单元测试展示了我们如何使用$eval: test/scope_spec.js it("execute $eval'ed function and retu