AngularJS作用域是一个指向应用模型的对象。它是表达式的执行环境。作用域有层次结构,这个层次和相应的DOM几乎是一样的。作用域能监控表达式和传递事件。
作用域是控制器和视图之间的“胶水”。在模板链接阶段,指令设置好作用域的$watch表达式。$watch使得指令能知晓属性的改变,这使得指令能重新渲染和更新DOM中的值。
控制器和指令都持有作用域的引用,但是不持有对方的引用。这使得控制器能从指令和DOM中脱离出来。这很重要,因为这使得控制器完全不需要知道view的存在,这大大改善了应用的测试。
举个例子:
script.js:
上例中MyController将值World赋给了作用域中的username。然后作用域将这个赋值的操作通知给input,然后input就会被渲染成预填充了值的样子。这展示控制器如何将数据写入到作用域。
同样的,控制器能将行为添加到作用域,正如你看到的sayHello方法,这个方法是在用户点击'greet'按钮时被调用的。
逻辑上来说,表达式{{greeting}}的渲染需要:
你可以把作用域和它的属性当做是用来渲染视图的数据。作用域是视图唯一相关联的变化来源。
每一个AngularJS应用都有一个绝对的根作用域。但是可能有多个子作用域。
一个应用可以有多个作用域,因为有一些指令会生成新的子作用域(参考指令的文档看看哪些指令会创建新作用域)。当新作用域被创建的时候,他们会被当成子作用域添加到父作用域下,这使得作用域会变成一个和相应DOM结构一个的树状结构。
当AngularJS执行表达式{{username}},它会首先查找和当前节点相关的作用域中叫做username的属性。如果没找到,那就会继续向上层作用域搜索,直到根作用域。在java script中,这被称为原型类型的继承,子作用域以原型的形式继承自父作用域。
下面这个例子展示了应用中的作用域,它们的继承关系。
script.js:
注意当作用域和元素相关联的时候,AngularJS会自动给相应元素添加ng-scope类名。这个例子中的作用域范围突出显示了。子作用域的存在是很有必要的,因为迭代器要执行{{employee.name}}表达式,它会根据不同的作用域生成不同的值。同样的,{{department}}的执行是继承自根作用域的,因为只有根作用域中定义了它。
作用域是作为$scope的数据属性关联到DOM上的,并且能在需要调试的时候被获取到。根作用关联的DOM就是ng-app指令定义的地方。一般来说ng-app都是放在元素中的,但是也能放在其他元素中。
在控制台中想获取关联的作用域:angular.element($0).scope()
作用域中的事件传递是和DOM事件传递类似的。事件可以广播给子作用域或者传递给父作用域。举个例子:
script.js:
浏览器接收到事件后的一般工作流程是执行一个相应的java script回调。回调一执行完,浏览器就会重新渲染DOM并且重新回到等待事件的状态。
当浏览器调用AngularJS上下文之外的java script代码时,AngularJS是不知道模型的更改的。要正确处理模型的更改,就要使用$apply方法进入AngularJS的执行上下文。只有在$apply方法内执行的模型修改才会正确地被AngularJS处理。比如,一个指令监听DOM事件,比如ng-click,它必须在$apply方法中来执行表达式。
执行完表达式之后,$apply会进入$digest阶段。在$digest阶段,作用域会检查所有的$watch表达式,并将它们和之前的值比较。这意味着赋值语句,如$scope.username="angular"不会马上导致$watch被通知,取而代之的是它会等到$digest阶段才被通知。这种方式是合理的,因为它将多个模型的更新整合到一个$watch通知里,并且保证了一个$watch通知期间不会有其他同样的$watch执行。
在模板编译阶段,编译器在DOM中匹配指令。指令通常分为两种:
当一个外界事件(比如用户操作,计时器或者XHR)触发时,相应的表达式必须在$apply()方法内,并由其相应的作用域调用,这样所有的监听者才会被正确地更新。
大部分情况下,指令和作用域交互,不会产生新的作用域实例。但是,有些指令,比如ng-controller和ng-repeat会创建新的作用域,并关联到相应的DOM元素上,你可以使用angular.element(aDomElement).scope()方法来获得某一个DOM元素相关的作用域。
作用域和控制器在以下几种情况下交互:
检测属性的改变是AngularJS中一项常用的操作,所以它应该是高效的。要注意的是,执行检测的方法不应该包含任何DOM操作,因为在java script对象中,DOM获取要比属性获取慢很多很多。
希望你喜欢,并分享我的工作~带你走近AngularJS系列: