你们都是到angularJS中很是屌的一个性能,数据双向绑定,这就意味着view中的数据发生任何变化的时候,这个变化也会相应的反映到scope上,也就是说scope的模型会动态更新。因此有时候咱们的需求多是要监控摸个model的变化,下面就简单的说下这个操做以及实现原理html
$watch
$watch能够很方便且简单的监控一个model的变化,举个栗子:java
<html> <head> <script src='./lib.angular.min.js'></script> </head> <body ng-app='watch'> <input ng-model='name' type='text'/> <div>change count: {{count}}</div> <script> angular.module('watch',[]) .run(['$rootScope',function($rootScope){ $rootScope.count = 0; $rootScope.name = 'Alfred'; $rootScope.$watch('name',function(){ $rootScope.count++; }) }]); </script> </body> </html>
上面这个代码就是用来监控name的变化的,每次当咱们在input输入框中输入一个值的时候,$rootScope中的count就会对应的+1;数组
在angularJS的内部,每当咱们对name的值进行修改的时候,angularJS内部中的$digest就会被调用一次,(下面在详细的说$digest的原理),并在运行结束以后检查咱们用$watch来监控的模型,如何和上一次执行$digest以前相比发生变化了,则执行$watch中的回调函数。app
然而!!!在咱们实际的开发中,仅仅实现对一个原始类型的数据监控是远远不可以知足所需的,对于原始类型的数据,若是咱们使用了一次赋值操做,则这个原始类型的数据变量会真正的别赋值一次,然而对于引用类型的变量,进行赋值时,仅仅是将赋值的变量指向了这个引用类型。
能够看下下面的栗子:异步
<html> <head> <script src='./lib.angular.min.js'></script> </head> <body ng-app='watch'> <div ng-repeat='item in items'> <input ng-model='item.a'/><span>{{item.a}}</span> </div> <div>change count: {{count}}</div> <script> angular.module('watch',[]) .run(['$rootScope',function($rootScope){ $rootScope.count = 0; $rootScope.items = [ { "a": 1 }, { "a": 2 }, { "a": 3 }, { "a": 4 } ] $rootScope.$watch('items',function(){ $rootScope.count++; }) }]); </script> </body> </html>
在这个栗子中咱们就会发现,无论咱们怎么改变其中的值,count都不会发生变化的。而这个就是咱们上面说那样,在说明这个以前,咱们在说明下$watch的第三个参数,通常$watch函数的前两个参数是必传的(监控对象,回调函数),第三个参数默认为false,这样的话咱们进行的监控叫作引用监控,这个意思就是监控对象的应用没有发生变化的时候就不算对象发生了变化,具体的来讲,上面的例子,就算items的属性发生了变化,只要items的引用没有发生变化,$watch就都当作没有看见,可是好比讲一个数组赋值给items时,这个时候$watch就看不下去了(给你阳光你就灿烂了还)。
相反,若是第三个参数设置为true的时候,那么咱们的监控叫作“全等监控”,此时的$watch的要求就是比较苛刻了,只要他监控的对象有一点点变化时,$watch就会跳出来,卧槽!你竟然还敢动!!!函数
固然值得提一下的是:为何第三个参数加个true,这么方便了咱们还不加呢?!固然是牵涉到性能的问题啦!全等监控运行起来的时候是先监控到整个对象,而后在每一次吧$diges跑起来以前先用angualr.copy()将整个对象先拷贝一遍以后再调用angular.equal()方法来进行比较,因此这一监控可能会消耗大量的资源!性能
在angularJS 1.1.4又出来了一个$watchCollection()方法,专门来监控数组集合的,他的性能介于引用监控和全等监控之间,它不会对数组的每一项内容 进行监控,而是当数组的pop和push时候作出反应。具体的栗子咱就很少赘述了。ui
下面谈一谈$apply
你们都知道他的做用是把改变同步绑定到界面上,可是它为何存在呢?何时须要用它呢?何时又不须要呢?
那么咱们首先说一下angular是如何进行数据双向绑定的吧。
要知道一个变量变了,方法无非就两种
一、 经过固定的接口,好比set,get方法,经过set设置变量的值,set被调用时作个比较就能够,可是这个方法和复杂!
二、 脏检查,将某一个对象复制一份快照,在某个时间,比较如今对象与快照的值。很明显,这个方法要复制两份对象,并且要遍历对象,比较每个属性。对!这样的确有性能问题!可是angular就是用这个的~
可是人家angular的脏检查不是对全部对象进行检查,只是当对象绑定到html中,该对象才复合检查对象(watcher),同理,angular对属性的脏检查也是如此。
看下watcher对象的源代码咱们就知道了:spa
watcher = { fn: listener, //监听回调函数 last: initWatchVal, //上一状态值 get: get, //取得监听的值 exp: watchExp, //监听表达式 eq: !!objectEquality //要不要比较引用 };
那么咱们何时去进行脏检查呢?
脏检查的点是在函数执行完以后,可是不标明异步调用也执行完毕,若是咱们的功能是异步的,那么咱们会发现咱们的改变并无更新到DOM上。
举个栗子:双向绑定
<!-- lang: js --> function Ctrl($scope) { $scope.message = "Waiting 2000ms for update"; setTimeout(function () { $scope.message = "Timeout called!"; // AngularJS unaware of update to $scope }, 2000); }
简单说dom上永远都不会显示Timeout called
固然,这个就是咱们$apply的应用场景了,调用它,手动触发脏检查,举个例子:angularJs提供了$timeout,为何咱有了setTimeout还要提供这个呢?就是应为$timeout异步完成后,angularJs会自动触发$apply
下面说下$apply的使用注意事项吧
在一个具备$apply的环境中使用apply,会抛出异常来的
因此若是说咱们的代码不在$apply环境中,结构是异步返回的,咱们就须要手动触发$apply
Apply接受一个函数做为参数,函数中别绑定的对象会被脏检查,function不能是异步的!固然,当apply函数的参数为空的是时候,它会把当前做用域中全部的脏对象都检查一遍。浪费性能!
然而这个脏检查有事怎么检查的呢??
好吧,这个就说到$digest函数了
$apply被调用后最终都会触发$digest()
在调用了 $scope.$digest() 后, $digest 循环就开始了。假设你在一个 ng-click 指令对应的 handler 函数中更改了 scope 中的一条数据,此时 AngularJS 会自动地经过调用 $digest() 来触发一轮 $digest 循环。当 $digest 循环开始后,它会触发每一个 watcher 。这些 watchers 会检查 scope 中的当前 model 值是否和上一次计算获得的 model 值不一样。若是不一样,那么对应的回调函数会被执行。调用该函数的结果,就是 view 中的表达式内容 ( 译注:诸如 {{ aModel }}) 会被更新。除了 ng-click 指令,还有一些其它的 built-in 指令以及服务来让你更改 models( 好比 ng-model , $timeout 等 ) 和自动触发一次 $digest 循环
暂且就说这么多吧,说多了也迷糊,不少都是须要本身去实际应用和体会的