AngularJS作用域是一个指向应用模型的对象。它是表达式的执行环境。作用域有层次结构,这个层次和相应的DOM几乎是一样的。作用域能监控表达式和传递事件。

作用域的特点

  • 作用域提供APIs($watch)来观察模型的变化。
  • 作用域提供APIs($apply)将任何模型的改变,反映到视图上。
  • 作用域能通过共享模型成员的方式嵌套到应用组件上。一个作用域从父作用域继承属性。
  • 作用域提供表达式执行的上下文。比如说表达式{{username}}本身是无意义的,除非把它放到指定username属性的作用域中。

作为数据模型的作用域

作用域是控制器和视图之间的“胶水”。在模板链接阶段,指令设置好作用域的$watch表达式。$watch使得指令能知晓属性的改变,这使得指令能重新渲染和更新DOM中的值。

控制器和指令都持有作用域的引用,但是不持有对方的引用。这使得控制器能从指令和DOM中脱离出来。这很重要,因为这使得控制器完全不需要知道view的存在,这大大改善了应用的测试。

举个例子:

<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="MyController">
Your name:
<input type="text" ng-model="username">
<button ng-click='sayHello()'>greet</button>
<hr>
{{greeting}}
</div>
</body>
</html>

script.js:

function MyController($scope) {
$scope.username = 'World'; $scope.sayHello = function() {
$scope.greeting = 'Hello ' + $scope.username + '!';
};
}

上例中MyController将值World赋给了作用域中的username。然后作用域将这个赋值的操作通知给input,然后input就会被渲染成预填充了值的样子。这展示控制器如何将数据写入到作用域。

同样的,控制器能将行为添加到作用域,正如你看到的sayHello方法,这个方法是在用户点击'greet'按钮时被调用的。

逻辑上来说,表达式{{greeting}}的渲染需要:

  • 获取模板中定义了{{greeting}}DOM节点相关的作用域。在这个例子里,就是传入到MyController的作用域。
  • 结合上一步获取到的作用域来计算表达式的值,将该值在DOM中替换掉表达式。

你可以把作用域和它的属性当做是用来渲染视图的数据。作用域是视图唯一相关联的变化来源。

作用域层级

每一个AngularJS应用都有一个绝对的根作用域。但是可能有多个子作用域。

一个应用可以有多个作用域,因为有一些指令会生成新的子作用域(参考指令的文档看看哪些指令会创建新作用域)。当新作用域被创建的时候,他们会被当成子作用域添加到父作用域下,这使得作用域会变成一个和相应DOM结构一个的树状结构。

当AngularJS执行表达式{{username}},它会首先查找和当前节点相关的作用域中叫做username的属性。如果没找到,那就会继续向上层作用域搜索,直到根作用域。在Javascript中,这被称为原型类型的继承,子作用域以原型的形式继承自父作用域。

下面这个例子展示了应用中的作用域,它们的继承关系。

<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
<script src="script.js"></script>
  <style>
    .doc-example-live .ng-scope {
        border: 1px dashed red;
    }
  </style>
</head>
<body>
<div ng-controller="EmployeeController">
Manager: {{employee.name}} [ {{department}} ]<br>
Reports:
<ul>
<li ng-repeat="employee in employee.reports">
{{employee.name}} [ {{department}} ]
</li>
</ul>
<hr>
{{greeting}}
</div>
</body>
</html>

script.js:

function EmployeeController($scope) {
$scope.department = 'Engineering';
$scope.employee = {
name: 'Joe the Manager',
reports: [
{name: 'John Smith'},
{name: 'Mary Run'}
]
};
}

注意当作用域和元素相关联的时候,AngularJS会自动给相应元素添加ng-scope类名。这个例子中的作用域范围突出显示了。子作用域的存在是很有必要的,因为迭代器要执行{{employee.name}}表达式,它会根据不同的作用域生成不同的值。同样的,{{department}}的执行是继承自根作用域的,因为只有根作用域中定义了它。

从DOM中获取作用域

作用域是作为$scope的数据属性关联到DOM上的,并且能在需要调试的时候被获取到。根作用关联的DOM就是ng-app指令定义的地方。一般来说ng-app都是放在<html>元素中的,但是也能放在其他元素中。

在控制台中想获取关联的作用域:angular.element($0).scope()

作用域事件的传递

作用域中的事件传递是和DOM事件传递类似的。事件可以广播给子作用域或者传递给父作用域。举个例子:

<!doctype html>
<html ng-app>
<head>
<script src="http://code.angularjs.org/angular-1.0.2.min.js"></script>
<script src="script.js"></script>
</head>
<body>
<div ng-controller="EventController">
Root scope <tt>MyEvent</tt> count: {{count}}
<ul>
<li ng-repeat="i in [1]" ng-controller="EventController">
<button ng-click="$emit('MyEvent')">$emit('MyEvent')</button> //当点击此按钮时,会触发MyEvent事件,这时会把此事件也传递给父作用域,也就是Root scope,这时它的count会增加1.当然同级的Middle scope的count也会加1.
<button ng-click="$broadcast('MyEvent')">$broadcast('MyEvent')</button> //当点击此按钮时,会触发MyEvent事件,这时会把此事件传递给子作用域,也就是Leaf scope,这时它的count会增加1,当然同级的Middle scope的count也会加1.
<br>
Middle scope <tt>MyEvent</tt> count: {{count}}
<ul>
<li ng-repeat="item in [1, 2]" ng-controller="EventController">
Leaf scope <tt>MyEvent</tt> count: {{count}}
</li>
</ul>
</li>
</ul>
</div>
</body>
</html>

script.js:

function EventController($scope) {
$scope.count = 0;
$scope.$on('MyEvent', function() { //监听MyEvent事件
$scope.count++;
});
}

作用域的声明周期

浏览器接收到事件后的一般工作流程是执行一个相应的Javascript回调。回调一执行完,浏览器就会重新渲染DOM并且重新回到等待事件的状态。

当浏览器调用AngularJS上下文之外的Javascript代码时,AngularJS是不知道模型的更改的。要正确处理模型的更改,就要使用$apply方法进入AngularJS的执行上下文。只有在$apply方法内执行的模型修改才会正确地被AngularJS处理。比如,一个指令监听DOM事件,比如ng-click,它必须在$apply方法中来执行表达式。

执行完表达式之后,$apply会进入$digest阶段。在$digest阶段,作用域会检查所有的$watch表达式,并将它们和之前的值比较。这意味着赋值语句,如$scope.username="angular"不会马上导致$watch被通知,取而代之的是它会等到$digest阶段才被通知。这种方式是合理的,因为它将多个模型的更新整合到一个$watch通知里,并且保证了一个$watch通知期间不会有其他同样的$watch执行。

  1. 创建——根作用域是在应用被$injector启动时创建的。在模板链接阶段,有些指令会创建新的子作用域。
  2. 观察者注册——在模板链接阶段,指令会在作用域上注册观察者。这些观察者是用来将模型的改变传递给DOM的。
  3. 模型变化——为了正确地观测到模型变化,你需要并且只能在scope.$apply()中改变他们。(AngularJS的API会隐式地这么做,所以在控制器或者在$http,$timeout等服务中你不需要额外的调用$apply)。
  4. 变化的观测——在$apply的最后,AngularJS会在根作用域中执行一个$digest循环,它会将变化传递给所有子作用域。在$digest循环中,所有的$watch表达式或者函数都会被检测,来观察模型的变化。如果有变化被检测到了,$watch的监听回调就会被调用。
  5. 作用域的销毁——如果子作用域不再有用了。那么子作用域的创建者就会负责用scope.$destroy() API来将它销毁。这会停止$digest再调用此子作用域,并且让此作用域占用的内容能够被回收。

在模板编译阶段,编译器在DOM中匹配指令。指令通常分为两种:

  • 观察型的指令,例如双花括号表达式{{expression}},会用$watch来注册一个监听者。无论表达式什么时候改变,这类型的指令都会被通知,并且能更新视图。
  • 监听者型的指令,比如ng-click,会向DOM注册一个监听者。当DOM监听者触发,指令会执行相关的表达式并且使用$apply方法更新视图。

当一个外界事件(比如用户操作,计时器或者XHR)触发时,相应的表达式必须在$apply()方法内,并由其相应的作用域调用,这样所有的监听者才会被正确地更新。

大部分情况下,指令和作用域交互,不会产生新的作用域实例。但是,有些指令,比如ng-controllerng-repeat会创建新的作用域,并关联到相应的DOM元素上,你可以使用angular.element(aDomElement).scope()方法来获得某一个DOM元素相关的作用域。

作用域和控制器在以下几种情况下交互:

  • 控制器通过作用域来向模板暴露方法(参考ng-controller
  • 控制器定义里能改变模型(作用域的属性)的方法(行为)
  • 控制器在模型上注册了观察者。这些观察者会在控制器行为执行后立即被执行

检测属性的改变是AngularJS中一项常用的操作,所以它应该是高效的。要注意的是,执行检测的方法不应该包含任何DOM操作,因为在Javascript对象中,DOM获取要比属性获取慢很多很多。

加油!

AngularJS开发指南9:AngularJS作用域的详解的更多相关文章

  1. AngularJS开发指南4:指令的详解

    指令是我们用来扩展浏览器能力的技术之一.在DOM编译期间,和HTML元素关联着的指令会被检测到,并且被执行.这使得指令可以为DOM指定行为,或者改变它. AngularJS有一套完整的.可扩展的.用来 ...

  2. 移动IM开发指南2:心跳指令详解

    <移动IM开发指南>系列文章将会介绍一个IM APP的方方面面,包括技术选型.登陆优化等.此外,本文作者会结合他在网易云信多年iOS IM SDK开发的经验,深度分析实际开发中的各种常见问 ...

  3. “全栈2019”Java第一百零五章:匿名内部类覆盖作用域成员详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  4. “全栈2019”Java第九十八章:局部内部类访问作用域成员详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  5. 最锋利的Visual Studio Web开发工具扩展:Web Essentials详解

    原文:最锋利的Visual Studio Web开发工具扩展:Web Essentials详解 Web Essentials是目前为止见过的最好用的VS扩展工具了,具体功能请待我一一道来. 首先,从E ...

  6. [ 转载 ] Java开发中的23种设计模式详解(转)

    Java开发中的23种设计模式详解(转)   设计模式(Design Patterns) ——可复用面向对象软件的基础 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类 ...

  7. AngularJS开发指南16:AngularJS构建大型Web应用详解

    AngularJS是由Google创建的一种JS框架,使用它可以扩展应用程序中的HTML功能,从而在web应用程序中使用HTML声明动态内容.在该团队工作的软件工程师Brian Ford近日撰写了一篇 ...

  8. AngularJS开发指南11:AngularJS的model,controller,view详解

    model model这个词在AngularJS中,既可以表示一个(比如,一个叫做phones的model,它的值是一个包含多个phone的数组)对象,也可以表示应用中的整个数据模型,这取决于我们所讨 ...

  9. AngularJS开发指南2:AngularJS初始化过程

    自动初始化 请将ng-app指令放到你应用的标签节点中, 如果你想要AngularJS自动执行整个<html>程序就把它放在 <html> 标签中.比如:<html ng ...

  10. AngularJS开发指南1:AngularJS简介

    什么是 AngularJS? AngularJS 是一个为动态WEB应用设计的结构框架.它能让你使用HTML作为模板语言,通过扩展HTML的语法,让你能更清楚.简洁地构建你的应用组件.它的创新点在于, ...

随机推荐

  1. HTTP详解1-工作原理

    1. HTTP简介 HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传送协议.它可以使浏览器更加高效,使网络传输减少. ...

  2. Centos 6.5(64bit)上安装Vertica single node

    在Win8上使用虚拟机Virtualbox安装Centos6.5,想在上面安装vertica. 以下记录了我在安装的过程中遇到的问题与一些解决方案. 1.安装Centos的时候遇到了一个恼人的问题,即 ...

  3. MySQL遇到check the manual that corresponds to your MySQL server version for the right syntax错误

    用MySQL新建了一个Order表,插入了一条数据.总是显示 You have an error in your SQL syntax; check the manual thatcorrespond ...

  4. 集合框架学习笔记<二>

    1.什么是ArrayList ArrayList就是传说中的动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了如下一些好处: 动态的增加和减少元素 实现了ICollection和ILis ...

  5. JavaWeb学习----JSP简介及入门(含Eclipse for Java EE及Tomcat的配置)

    ​[声明] 欢迎转载,但请保留文章原始出处→_→ 生命壹号:http://www.cnblogs.com/smyhvae/ 文章来源:http://www.cnblogs.com/smyhvae/p/ ...

  6. Java基础详解 (一)Java的类成员访问权限修饰词(以及类访问权限)

    在一个类的内部,其成员(包括成员变量和成员函数)能否被其他类所访问,取决于该成员的修饰词.Java的类成员访问权限修饰词有四类:private,无(默认情况下),protected和public.其权 ...

  7. 2014Ember带来怎样的变化?

    每隔几个月的时间,Ember的核心团队就会聚在一起讨论目前遇到的各种问题,并决定下一季度需要优先处理的各种事务. 这一次,在俄勒冈州的波特兰,大家聚在一起,商讨2014年的发展方向. 开发工具 &am ...

  8. javascript中的array对象属性及方法

    Array 对象 Array 对象用于在单个的变量中存储多个值. 创建 Array 对象的语法: new Array(); new Array(size); new Array(element0, e ...

  9. 如何将NTFS格式的移动硬盘挂接到Mac OS上进行读写(Read/Write)操作

    现在硬盘便宜,很多同学都有移动硬盘,如果你同时使用Windows与Mac OS的话,移动硬盘最好不要使用NTFS文件系统,否则在Mac OS上,你只能读你的移动硬盘,不能写. 但是实际上的情况是,移动 ...

  10. Http请求中POST与GET的区别——前端面试

    一.原理区别 Http定义了与服务器交互的方法,其中最基本的四种是:GET,POST,PUT,DELETE,正对应着对资源的查,改,增,删.URL的全称是资源描述符,我们可以这样认为,一个URL地址, ...