本篇文章参考引用了AngluarJS权威教程。
本篇文章参考引用的博客http://www.lovelucy.info/understanding-scopes-in-angularjs.html。
1.简介
作用域是构成AngularJS应用的核心,它与应用的数据模型相关联,同时也是表达式执行的上下文。作用域包含了渲染视图所需的功能和数据,它是所有视图的唯一源头,可以将作用域理解成视图模型。
$scope对象是定义应用业务逻辑、控制器方法和视图属性的地方。AngularJS将$scope设计成和DOM类似的结构,因此$scope可以进行嵌套,也就是说我们可以引用父级$scope中引用父级$scope中的属性和方法。
2.作用域功能
- 提供观察者以监视数据模型的变化。
- 可以将数据模型的变化通知给整个应用,设置是系统外的组件。
- 可以进行嵌套,隔离业务功能和数据。
- 给表达式提供运算时所需的执行环境。
3.作用域的嵌套
AngularJS启动并生成视图时,会将根ng-app元素同$rootScope进行绑定,$rootScope是所有$scope的父亲。下面来看一个作用域嵌套的例子。
<div ng-controller="FatherController">
<p>{{"父作用域下name的值为:"+ name}}</p>
<p>{{"父作用域下age的值为:"+ model.age}}</p>
<div ng-controller="ChildController">
<p>{{"子作用域下name的值为:"+ name}}</p>
<p>{{"子作用域下age的值为:"+ model.age}}</p>
<input type="button" ng-click="change1()" value="改变方式1"/>
<input type="button" ng-click="change2()" value="改变方式2"/>
</div>
</div>
<script>
var app = angular.module("myApp",['ionic']); app.controller('FatherController',function($scope) {
$scope.name = "张三";
$scope.model = {age:15};
});
app.controller('ChildController',function($scope) {
$scope.change1 = function() {
$scope.name = "李四";
$scope.model.age = 18;
};
$scope.change2 = function() {
$scope.model = {age:21};
};
});
</script>
运行,刚开始虽然我们没给子作用域赋值,但是输出结果显示它不仅有值,还与父作用域值相同。这是因为当javaScript在子Scope中查找不到相应的属性,会到父Scope中查找,如果仍然找不到,会不断往上,直到rootScope。
接下来点击一下改变方式1的按钮,我们给子作用域下的name和model.age赋值,发现age在父作用域修改了而name并没有修改。这是由于JavaScript原型链的工作原理导致的。如果你在子作用域下为与父作用域的同名属性赋值,那么新属性将会覆盖从父作用域下继承的属性,正如此例子中的name一样。而age的修改是在model下执行的,我们并没有为子作用域增加一个model属性,修改的是父作用域下的model,当我们子作用域下再次访问model,将会向上找到父作用域的model。
最后点击一下改变方式2的按钮,这次是给子作用域中添加了一个model属性,和我们上面分析的一样,这次两个age的值不一样了,它们分别是两个作用域里的两个不同的属性,而不是通过原型链查找的同一个属性。
4.会创建子作用域的指令
AngularJS中大部分指令不会创建子作用域,但是以下指令例外,开发时必须注意作用域嵌套的相关问题,否则很容易出错。
- ng-include
创建一个新的子作用域,并且会继承父作用域。 - ng-switch
创建一个新的子作用域,并且会继承父作用域。 - ng-repeat
为每一个item都创建一个子作用域,全部都会继承父作用域。 - ng-controller
创建一个新的作用域,并且会继承父作用域。
关于自定义指令
这个要看scope属性如何设置,默认scope为false,不会创建新的Scope,如果设置为true,那么会创建一个子Scope并且继承父Scope。除了true和false以外,还可以将scope属性设置为一个对象,这样将会创建一个隔离作用域,这会使指令的模板无法访问外部作用域(可以在对象中声明特定的绑定策略)。
下面提供一个scope设置为true的例子。这个例子证明了scope为true时确实创建了一个新的子作用域,而且继承了父作用域。
<div ng-controller="myController">
<p>{{text}}</p>
<my-directive>
<p>{{text}}</p>
<p>在下方修改动物:<input type="text" ng-model="text"/></p>
<input type="button" ng-click="change()" value="改变"/>
</my-directive>
</div>
<script>
var app = angular.module("myApp",['ionic']);
app.directive('myDirective',function() {
return {
restrict:"E",
scope:true
};
});
app.controller('myController',function($scope) {
$scope.text = "猫";
$scope.change = function() {
$scope.text= "狗";
};
});
</script>
5.$scope生命周期
$scope对象的生命周期处理有四个不同阶段,分别是创建,链接,更新和销毁。
1) 创建
在创建控制器或指令时,AngularJS会用$injector创建一个新的作用域,并在这个新建的控制器或指令运行时将作用域传递进去。
2) 链接
当Angular开始运行时,所有的$scope对象都会附加或者链接到视图中。所有创建$scope对象的函数也会将自身附加到视图中。这些作用域将会注册当Angular应用上下文中发生变化时所需要运行的函数。这些函数被称为$watch函数,Angular通过这些函数获知何时启动时间循环。
3)更新
当事件循环运行时,它通常执行在$rootScope上。每个子作用域都执行自己的脏值检测,每个监控函数都会检查变化。如果检测到任意变化,$scope对象就会触发指定的回调函数。
4)销毁
当一个$scope在视图中不再需要时,这个作用域将会清理和销毁自己。(基本上不会需要你来清理作用域,但是你还是可以使用$scope上的$destroy()方法来清理作用域)。