1、AngularJS Scope(作用域)
Scope(作用域)是应用在 HTML (视图)和 JavaScript (控制器)之间的纽带。Scope是一个对象,有可用的方法和属性。Scope可应用在视图和控制器上。$scope的使用贯穿整个 Angular App应用,它与数据模型相关联,同时也是表达式执行的上下文.有了 $scope就在视图和控制器之间建立了一个通道,基于作用域视图在修改数据时会立刻更新 $scope,同样的 $scope发生改变时也会立刻重新渲染视图.
2、根作用域 rootScope
所有的应用都有一个 $rootScope,它可以作用在 ng-app指令包含的所有 HTML元素中。$rootScope可作用于整个应用中。是各个 controller中 scope的桥梁。用 rootscope定义的值,可以在各个 controller中使用
1、$scope
$scope是一个把view(一个DOM元素)连结到controller上的对象。在我们的MVC结构里,这个 $scope将成为model,它提供一个绑定到DOM元素(以及其子元素)上的excecution context。
$scope实际上就是一个JavaScript对象,controller和view都可以访问它,所以我们可以利用它在两者间传递信息。在这个 $scope对象里,我们既可以存储数据,又可以存储将要运行在view上的函数。每一个Angular应用都会有一个$rootScope。这个$rootScope 是最顶级的scope,它对应着含有ng-app 指令属性的那个DOM元素。如果页面上没有明确设定$scope ,Angular 就会把数据和函数都绑定到这里。
Angular应用启动并生成视图时,会将根 ng-app元素与 $rootScope进行绑定.$rootScope是所有 $scope的最上层对象,可以理解为一个 Angular应用中得全局作用域对象,所以不应该附加太多逻辑或者变量给$rootScope,和污染 Javascript全局作用域是一样的道理.
$scope的作用
$scope对象在 Angular中充当数据模型的作用,也就是一般 MVC框架中 Model得角色.但又不完全与通常意义上的数据模型一样,因为 $scope并不处理和操作数据,它只是建立了视图和 HTML之间的桥梁,让视图和 Controller之间可以友好的通讯。
它有如下作用和功能:
提供了观察者可以监听数据模型的变化
可以将数据模型的变化通知给整个 App
可以进行嵌套,隔离业务功能和数据
给表达式提供上下文执行环境
在 Javascript中创建一个新的执行上下文,实际就是用函数创建了一个新的本地上下文,
在 Angular中当为子 DOM元素创建新的作用域时,其实就是为子 DOM元素创建了一个新的执行上下文.
$scope的生命周期有4个阶段:
1.创建
控制器或者指令创建时, Angular会使用 $injector创建一个新的作用域,然后在控制器或指令运行时,将作用域传递进去.
2.链接
Angular启动后会将所有 $scope对象附加或者说链接到视图上,所有创建 $scope对象的函数也会被附加到视图上.
这些作用域将会注册当 Angular上下文发生变化时需要运行的函数.也就是 $watch函数, Angular通过这些函数或者何时开始事件循环.
3.更新
一旦事件循环开始运行,就会开始执行自己的脏值检测.一旦检测到变化,就会触发 $scope上指定的回调函数
4.销毁
通常来讲如果一个 $scope在视图中不再需要, Angular会自己清理它.
ng-controller指令给所在的DOM元素创建了一个新的$scope对象,并将这个$scope对象包含进外层DOM元素的$scope对象里。
在ng-app里,这个外层DOM元素的$scope对象,就是$rootScope对象。这个scope链是这样的:
所有scope都遵循原型继承(prototypal inheritance),这意味着它们都能访问父scope们。对任何属性和方法,如果AngularJS在当前scope上找不到,就会到父scope上去找,如果在父scope上也没找到,就会继续向上回溯,一直到$rootScope上。唯一的例外:有些指令属性可以选择性地创建一个独立的scope,让这个scope不继承它的父scope们。
3、$watch:
angularjs核心之一是双向绑定,那么这个双向绑定是如何实现的呢? 当我们在创建出scope下的一个新属性的时候,ng就会主动为我们新属性注册$watch这个方法,$watch用来监听的数据变化,当数据变化之后,就立即把view和scope上数据同步。AngularJS就能够自动注册并监听变量的改变。AngularJS会首先将在{{ }}中声明的表达式编译成函数并调用$watch方法。
$watch是一个scope函数,用于监听模型变化
$watch(watchExpression, listener, objectEquality){ ... };
watchExpression:$watch方法的第一个参数是一个函数,它通常被称为watch函数,它的返回值声明需要监听的变量;
listener:第二个参数是listener,在变量发生改变的时候会被调用。和传统的事件注册和监听没有什么本质上的差别,差别仅在于AngularJS能够自动注册绝大多数的change事件并进行监听,只要按照AngularJS要求的语法来写HTML中的表达式代码,即{{ }}。 $watch方法为当前scope注册了一个watcher,这个watcher会被保存到一个scope内部维护的数组中,即是$$watchers。 watcher的主要目的是对scope上的某个属性进行监控
objectEquality:是否深度监听,如果设置为true,它告诉Angular检查所监控的对象中每一个属性的变化. 当浏览器接收到可以被angular context处理的事件时,$digest循环就会触发。这个循环是由两个更小的循环组合起来的。 一个处理evalAsync队列(这个没有探究),另一个处理$watch队列。$digest将会遍历我们的$watch队列。如果有至少一个更新过, 这个循环就会再次触发,直到所有的$watch都没有变化。这样就能够保证每个model都已经不会再变化。 如果循环超过10次的话,它将会抛出一个异常,防止无限循环。每次当$digest循环结束时,DOM相应地变化。
例如我们按下按钮触发ng-click事件:
1、浏览器接收到一个事件,进入angular context。
2、 $digest循环开始执行,查询每个$watch是否变化。
3、 由于监视$scope.name的$watch报告了变化,它会强制再执行一次$digest循环。
4、 新的$digest循环没有检测到变化。
5、浏览器拿回控制权,更新与$scope.name新值相应部分的DOM。
6、这里重要的是每一个进入angular context的事件都会执行一个$digest循环,也就是说每次我们输入一个字母循环都会检查整个页面的所有$watch。 Angular会为我们自动调用$apply!因此当点击带有ng-click的元素时,事件就会被封装到一个$apply调用。 比如有一个ng-model="foo"的输入框,然后敲一个f,事件就会这样调用$apply("foo = 'f';"),触发$digest循环。
4、$state
$state是ui-rooter的一项服务负责表示状态以及它们之间的转换。它还提供了接口来询问当前状态
常用的方法有:
$state.go(to, params, options) :转换到新状态的方便方法
$state.includes(stateOrName, params, options) :确定当前活动状态是否等于或是状态状态子的方法。返回布尔值
$state.params: 返回状态参数的对象$stateParams
$stateParams是一个对象,包含 url中每个参数的键/值。$stateParams可以为控制器或者服务提供 url的各个部分。
注意:$stateParams必须与一个控制器相关,并且$stateParams中的“键/值”也必须事先在那个控制器的url属性中有定义。
我们都知道过多的watch会造成性能问题,那么如何移除不必要的$watch?
.controller('MainCtrl', function($scope) {
$scope.updated = 0;
$scope.stop = function() {
textWatch();
};
var textWatch = $scope.$watch('text', function(newVal, oldVal) {
if (newVal === oldVal) { return; }
$scope.updated++;
});
});
什么情况下用watch?
angular会为我们自动执行$watch,当指令中有独立作用域,或者在异步函数中,改变的数据不在angular的执行上下文,就需要手动调用$apply 来触发$digest去执行$watch
我个人的实践一般是用于观察数据中某个值的变化做出一些行为而使用。