对于Angular JS中$apply()的理解

最近在学angularjs知识,在网上找到这篇文章,来自于:http://my.oschina.net/u/1402334/blog/500683,解决了我目前面临的一些问题,感觉收获很大,特此转过来做个记录:

Angular最引人注目的特性就是双向绑定,然而它是怎么做到的,我可以来总结两点:

  • 将变化的数据从model传向view-->$apply
  • 将变化的数据从view传向model-->$watch

这连个函数都是基于scope的基础上,对scope对象的成员变化状况进行传播的。那么,我不妨是从scope开始。那么什么是scope呢?借用官方文档的一段话:

“scope is an object that refers to the application model. It is an execution context for expressions. Scopes are arranged in hierarchical structure which mimic the DOM structure of the application. Scopes can watch expressions and propagate events.”

看完后,类比到其他的编程语言上,感觉Scope就像是Data Model的作用域一样,为Expressions的执行提供上下文。

Scope的特性

  接下来,看看Scope有哪些特性呢?

  • Scope提供$watch方法监视Model的变化。
  • Scope提供$apply方法传播Model的变化。
  • Scope可以继承,用来隔离不同的application components和属性访问权限。
  • Scope为Expressions的计算提供上下文。

对于这四点特性,因为我之前学习过ActionScript、C++、Java,所以第一、三、四点不难理解,唯独第二点感觉有点云里雾里。本着打破沙锅问到底的原则,我还是通过Google搜到了一些东西。

源起Javascript

首先,乍一看,$scope.$apply()似乎就是一个使得bindings得到更新的普普通通的方法。但稍微多想一点,为什么我们需要它?一般在什么时候用它呢?用弄明白这两个问题,还得从Javascript说起。在Javascript代码里,都是按照一定顺序来执行的,当轮到一个代码片段执行的时候,浏览器就只会去执行当前的片段,不会做任何其他的事情。所以有时候一些做得不是很好的网页,当点击了某个东西之后会卡住,Javascript的工作方式就是会导致这一现象原因之一!下面我们有一段代码来感受一下:

var button = document.getElementById(‘clickMe‘);

function buttonClicked () {
  alert(‘the button was clicked‘);
}

button.addEventListener(‘click‘, buttonClicked);
function timerComplete () {
  alert(‘timer complete‘);
}

setTimeout(timerComplete, 5000);

  

当加载Javascript代码时,先先找一个一个id叫“clickMe”的按钮,然后添加一个监听器,然后设置超时。等待5秒,会弹出一个对话框。如果刷新页面并立即点击clickMe按钮,会弹出一个对话框,如果你不点击OK,timerComplete函数永远没有机会执行。那就是因为成员的变化没有被更新

如何更新bindings

好了,扯了一些看似不相关的东西之后,我们回归正题。angular JS是怎么知道什么时候数据的变化和页面需要更新的呢?代码需要知道什么时候数据被修改了,但是现在又没有一种方法直接去通知说某个对象上的数据变了(尽管ECMAScript 5正在尝试解决这一问题,但也还是处于实验阶段)。而目前比较主流的策略有以下有两种解决方案。一种是需要用特殊的对象,让所有的数据都只能通过调用对象的方法设置,而不是直接通过property指定。这样的话,所有的修改就可以被记录下来了,就知道什么时候页面需要更新了。这样做的弊端就是我们必须去继承一个特殊的对象。对于赋值也只能通过object.set(‘key‘, ‘value‘)而不是object.key=value的方式。在框架中,像EmberJS和KnockoutJS就是这么干的(虽然我都没接触过——囧)。另一种就是angular JS采用的方式,在每一次Javascript代码执行序列执行结束后都去检查是否有数据的改变。这看起来似乎并不高效,甚至严重影响性能。但是angular JS采用了一些比较巧妙的手段解决了这个问题(以后有机会好好研究)。这么做的好处就是,我们可以随便使用任意对象,对于赋值方式也没有限制,而且对于数据的改变也能觉察到。

对于angular JS采取的这种解决方案,我们关心的是什么时候数据发生了变化,而这也正是$scope.$apply()派上用场的地方。对于检查绑定的数据到底有没有发生变化,实际上是由$scope.$digest()完成的,但是我们几乎从来就没有直接调用过这个方法,而是调用$scope.$apply()方法,是因为在$scope.$apply()方法里面,它会去调用$scope.$digest()方法。$scope.$apply()方法带一个函数或者一个表达式,然后执行它,最后调用$scope.$digest()方法去更新bindings或者watchers。

什么时候用$apply()

还是那个问题,那我们到底什么时候需要去调用$apply()方法呢?情况非常少,实际上几乎我们所有的代码都包在$scope.$apply()里面,像ng-click,controller的初始化,$http的毁掉函数等。在这些情况下,我们不需要自己调用,实际上我们也不能自己调用,否则在$apply()方法里面再调用$apply()方法会抛出错误。如果我们需要在一个新的执行序列中运行代码时才真正需要用到它,而且当且仅当这个新的执行序列不是被angular JS的库的方法创建的,这个时候我们需要将代码用$scope.$apply()包起来。下面用一个例子解释:

[HTML]

<div ng:app ng-controller="Ctrl">{{message}}</div>

  

[Javascript]

functionCtrl($scope) {

  $scope.message ="Waiting 2000ms for update";    

  setTimeout(function () {

    $scope.message ="Timeout called!";

     // AngularJS unaware of update to $scope

  }, 2000); 

}

  

上面的代码执行后页面上会显示:Waiting 2000ms for update。显然数据的更新没有被angular JS觉察到。

接下来,我们将Javascript的代码稍作修改,用$scope.$apply()包起来。

[Javascript]

functionCtrl($scope) {

  $scope.message ="Waiting 2000ms for update"; 

  setTimeout(function () {

    $scope.$apply(function () {

       $scope.message ="Timeout called!";

      });

  }, 2000); 

}

  

这次与之前不同的是,页面上先会显示:Waiting 2000ms for update,等待2秒后内容会被更改为:Timeout called! 。显然数据的更新被angular JS觉察到了。

NOTE:我们不应该这样做,而是用angular JS提供的$timeout方法,这样它就会被自动用$apply方法包起来了。

科学是把双刃剑

最后,我们再来瞅一眼$scope.$apply()和$scope.$apply(function)方法吧!虽然angular JS为我们做了很多事情,但是我们也因此丢失了一些机会。从下面的伪代码一看便知:

function$apply(expr) {

  try {

    return$eval(expr);

  } catch(e) {

    $exceptionHandler(e);

  } finally {

    $root.$digest();

  }

}

  

  它会捕获所有的异常并且不会再抛出来,最后都会调用$digest()方法。

---恢复内容结束---

时间: 11-16

对于Angular JS中$apply()的理解的相关文章

angular js 中$apply()的使用

angular js的双向数据绑定,在开发中起到的作用灰常大,但是,并不是所有时候都能起作用. 找了下资料发现,双向数据绑定其实也就是当模型发生了变化的时候,重绘了DOM,使你看到数据被更新了,引发模型变化的情况有: 1,dom事件: 2,xhr响应触发回调: 3,浏览器的地址变化: 4,计时器触发回调: 以上的某一个情况发生,都会触发模型监控机制,同时调用了$apply方法,重绘了dom;通常情况下,我们使用的一些指令或服务,如$http,$timeout,$location等都会调用$app

对angular.js的一点理解

最近一直在学习angular.js.不得不说和jquery相比有很大不同,有很多的不同点,之前也用过Knockout.js 但是两者还是有一定的区别的,首先knockout.js是基于Mvvm的,是几乎纯粹的dom 绑定没有一个架构一样的东西.而angular.js 则有dom 绑定还有mvc 架构的支持.一 angular.js 和以前的js 框架有的区别:    1 传统的前端开发思维:       以jquery 为例,它是以dom 为中心的,是dom 元素的操作,更多的是关注dom 的变

angular.js 中同步视图和模型数据双向绑定,$watch $digest $apply 机制

Angular.js 中的特性,双向绑定. 让视图的改变直接反应到数据中,数据的改变又实时的通知到视图,如何做到的? 这要归功于 scope 下面3个重要的方法: $watch $digest $apply 他们的区别是什么,我们来介绍下: $watch 这是一个监听 scope 上数据的监听器 方法说明: $scope.$watch('参数',function(newValue,oldValue){ //逻辑处理 }) 上面我们就是创建了一个监听器. ‘参数’ 就是$scope对象下的一个对象

angular.js的路由和模板在asp.net mvc 中的使用

我们知道angular.js是基于mvc 的一款优秀js框架,它也有一套自己的路由机制,和asp.net mvc 路由不太一样.asp.net mvc 的路由是通过不同的URL到不同的controller然后交给controller去呈现视图.但是在angular.js则是需要提前指定一个module(ng-app),然后去定义路由规则,通过不同的URL,来告诉ng-app 去加载哪个页面.再渲染到ng-view.通过angular.js路由的使用,可以很容易实现页面的局部刷新.更加高效的去创建

关于JS中apply方法的基本理解

最近研究OpenLayers源码时,发现其中使用了比较多的apply方法,对其也是很不明白.于是上网经过多方面了解以及自己细细体会后,终于算是基本明白是其干什么的了,这里分享下.apply方法的造型是这样的,Function.apply(obj,args) 能接收两个参数:obj:这个对象将代替Function类里this对象args:这个是数组,它将作为参数传递.按网上大牛的解释是apply:劫持另外一个对象(a)的方法,继承a的属性.为了进一步理解,我自己做了下小例子,如下: (functi

angular js的Inline Array Annotation的理解

inline Array annotation的形式是: someModule.controller('MyController', ['$scope', 'greeter', function($scope, greeter) { // ... }]);其中function($scope,greeter){}分别代表$scope对象,greeter对象. 现在我把上面的代码改动一点: someModule.controller('MyController', ['$scope', 'greet

Angular JS中$timeout的用法及其与window.setTimeout的区别

$timeout的用法 angular.js的$timeout指令对window.setTimeout做了一个封装,它的返回值是一个promise对象.当定义的时间到了以后,这个promise对象就会被resolve,回调函数就会被执行. 如果需要取消一个timeout,调用$timeout.cancel(promise)方法. 用法: $timeout(fn, [delay], [invokeApply]): fn: 回调函数(必填) delay: number类型.延迟的时间(非必填),如果

Angular JS赶快学起来(上)

               Angular JS赶快学起来(上) 我将分为上下两篇来介绍Angular JS,废话不多说,直接看内容吧... 一:首先,什么是Angular JS呢? - 一个前端框架,提供一种无DOM操作的编程方式,在前端页面中引入了传统在后台开发中使用的一些思想,增强代码的结构和可维护性 - 一个功能非常完备的前端框架,通过增强HTML的方式提供一种便捷开发Web应用程序的方式 - 其核心特点就是几乎无任何DOM操作,让开发人员的精力和时间全部集中于业务 - MVC的特性增

Angular.JS------认识Angular.JS

之前在C#中也经常使用MVC开发模式,对其的使用有一定的了解,但是现在需要学习AngularJS,这是纯前台的JS代码,那么为什么需要学习这个呢,就是想将验证这里全部在前台执行,不需要在后台代码里面出现验证.项目需要,自己还是得下功夫看下,不要求全部会,简单的使用就OK. AngularJS最适于开发客户端的单页面应用,不是功能库,是用来开发动态网页的框架,专注于扩张HTML的功能,提供动态数据绑定.依赖注入,没想到这里也有这个,之前是在IOC中听到过,自己也研究了下,但是对其理解不是很好,这里