angularJS中$apply()方法详解

对于一个在前端属于纯新手的我来说,Javascript都还是一知半解,要想直接上手angular JS,遇到的阻力还真是不少。不过我相信,只要下功夫,即使是反人类的设计也不是什么大的问题。

Okay,废话不多说。为了弄明白angular JS为何物,我先是从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()包起来。下面用一个例子解释:

代码如下:

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

代码如下:

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()包起来。

代码如下:

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()方法。

总结一下

  $apply()方法可以在angular框架之外执行angular JS的表达式,例如:DOM事件、setTimeout、XHR或其他第三方的库。这仅仅是个开始,水还有很深,欢迎大家一起来deep dive!

(0)

相关推荐

  • Angularjs中UI Router全攻略

    首先给大家介绍angular-ui-router的基本用法. 如何引用依赖angular-ui-router angular.module('app',["ui.router"]) .config(function($stateProvider){ $stateProvider.state(stateName, stateCofig); }) $stateProvider.state(stateName, stateConfig) stateName是string类型 stateCon

  • AngularJS内置指令

    指令,我将其理解为AngularJS操作HTML element的一种途径. 由于学习AngularJS的第一步就是写内置指令ng-app以指出该节点是应用的根节点,所以指令早已不陌生. 这篇日志简单记录了一些内置指令,先使用起来,再谈一些有趣的东西. 内置指令 所有的内置指令的前缀都为ng,不建议自定义指令使用该前缀,以免冲突. 首先从一些常见的内置指令开始. 先列出一些关键的内置指令,顺便简单说说作用域的问题. ng-model 将表单控件和当前作用域的属性进行绑定,这么解释似乎也不太正确.

  • angularjs指令中的compile与link函数详解

    通常大家在使用ng中的指令的时候,用的链接函数最多的是link属性,下面这篇文章将告诉大家complie,pre-link,post-link的用法与区别. angularjs里的指令非常神奇,允许你创建非常语义化以及高度重用的组件,可以理解为web components的先驱者. 网上已经有很多介绍怎么使用指令的文章以及相关书籍,相互比较的话,很少有介绍compile与link的区别,更别说pre-link与post-link了. 大部分教程只是简单的说下compile会在ng内部用到,而且建

  • AngularJS 日期格式化详解

    AngularJS是为了克服HTML在构建应用上的不足而设计的.HTML是一门很好的为静态文本展示设计的声明式语言,但要构建WEB应用的话它就显得乏力了.所以我做了一些工作(你也可以觉得是小花招)来让浏览器做我想要的事. AngularJS的日期格式化有两种形式,一种是在HTML页面,一种是在JS代码里,都是用到AngularJS的过滤器$filter. HTML: date_expression 即 你在$scope中设的date类型变量(注意,一定是date object才正确), 也是要显

  • AngularJs根据访问的页面动态加载Controller的解决方案

    用Ng就是想做单页面应用(simple page application),就是希望站内所有的页面都是用Ng的Route,尽量不用location.href,但是这样的webapp好处是很多,但是美中不足的是当你的webapp随着时间的推移,用户变多,功能变得更丰富,controller也变得越来越多,你不得不把所有的controller当作全局模块进行加载,以使得在站内任何一个页面中按F5刷新后能route到任意一个其他页面,而不会发生找不到controller的错误,加载所有的control

  • jQuery和AngularJS的区别浅析

    最近一直在研究angularjs,最大的感受就是它和之前的jQuery以及基于jQuery的各种库设计理念完全不同,如果不能认识到这点而对于之前做jQuery开发的程序员,去直接学习angularjs的话,很可能学了很久还不知道这个东西能用来干什么以及怎么使用,怎么和UI进行结合等问题,在stackoverflow上找到一篇关于这方面的文章,阅读之后颇有收获,在此基础上将它译成中文,以求抛砖引玉大家一同学习. 原问题:假如我熟悉利用jQuery去开发客户端应用,那么我怎么上手angularjs,

  • angular.element方法汇总

    addClass()-为每个匹配的元素添加指定的样式类名 after()-在匹配元素集合中的每个元素后面插入参数所指定的内容,作为其兄弟节点 append()-在每个匹配元素里面的末尾处插入参数内容 attr() - 获取匹配的元素集合中的第一个元素的属性的值 bind() - 为一个元素绑定一个事件处理程序 children() - 获得匹配元素集合中每个元素的子元素,选择器选择性筛选 clone()-创建一个匹配的元素集合的深度拷贝副本 contents()-获得匹配元素集合中每个元素的子元

  • Angular动态添加、删除输入框并计算值实例代码

    Angular动态添加.删除输入框并计算值实例代码 摘要: 在学习群中交流时,有人分享了一个动态添加输入框的方法,我在其基础上进行了一些改进 这个功能本身并不复杂,但还是要注意,每个ng-model的对象必须是不同的,这样才能把它们分隔开. 下面是完整代码: JS: angular.module("myApp",[]) .controller("inputController",function($scope){ $scope.items=[]; //初始化数组,以

  • angularJS中$apply()方法详解

    对于一个在前端属于纯新手的我来说,Javascript都还是一知半解,要想直接上手angular JS,遇到的阻力还真是不少.不过我相信,只要下功夫,即使是反人类的设计也不是什么大的问题. Okay,废话不多说.为了弄明白angular JS为何物,我先是从Scope开始.那么什么是Scope呢?借用官方文档的一段话: 复制代码 代码如下: "scope is an object that refers to the application model. It is an execution c

  • Java Fluent Mybatis 聚合查询与apply方法详解流程篇

    前言 接着上一篇文章:Java Fluent Mybatis 分页查询与sql日志输出详解流程篇 我把分页已经调整好了,现在实验一下官方给出的聚合查询方法. GitHub代码仓库:GitHub仓库 数据准备 为了聚合查询的条件,添加了几条数据. MIN 我们试着获取最小的年龄. 方法实现 @Override public Integer getAgeMin() { Map<String, Object> result = testFluentMybatisMapper .findOneMap(

  • AngularJS封装指令方法详解

    本文实例讲述了AngularJS封装指令方法.分享给大家供大家参考,具体如下: 引言:angularjs是一个中等重量级的前端开发框架 HTML是一门很好的为静态文本设计的语言,但要构建动态的web应用它就显的乏力了.通常,我们使用以下技术来解决静态网页技术在构建动态应用上的不足: 1.类库:类库是一类函数的集合,它能帮助你写web应用.这里起主导作用是你的代码,由你来决定何时使用类库.典型的类库,例如prototype.jQuery等. 2.框架:框架式一种特殊的.已经实现的web应用,你只需

  • Js apply方法详解

    我在一开始看到javascript的函数apply和call时,非常的模糊,看也看不懂,最近在网上看到一些文章对apply方法和call的一些示例,总算是看的有点眉目了,在这里我做如下笔记,希望和大家分享..  如有什么不对的或者说法不明确的地方希望读者多多提一些意见,以便共同提高.. 主要我是要解决一下几个问题: 1.apply和call的区别在哪里  2.什么情况下用apply,什么情况下用call  3.apply的其他巧妙用法(一般在什么情况下可以使用apply)  我首先从网上查到关于

  • JS中call/apply、arguments、undefined/null方法详解

    a.call和apply方法详解 -------------------------------------------------------------------------------- call方法: 语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 定义:调用一个对象的一个方法,以另一个对象替换当前对象. 说明: call 方法可以用来代替另一个对象调用一个方法.call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指

  • AngularJS的$location使用方法详解

    AngularJS的$location使用方法详解 一.配置config app.config([ '$locationProvider', function($locationProvider) { $locationProvider.html5Mode({ //设置为html5Mode(模式),当为false时为Hashbang模式 enabled : true, //是否需要加入base标签,这里设置为false,设置为true时,需在html的head配置<base href="&

  • angularjs中的$eval方法详解

    在controller中定义了一个变量 $scope.a_1 = "abc"; 想在view里面动态输出,因为这个数字是动态的,这么输出肯定是不行的{{'a_' + '1'}},因为输出来的是a_1这个字符串,而不是a_1这个变量的值 想输出a_1这个变量的值,可以使用$eval方法:{{$eval('a_' + '1')}} $eval是作为scope的方法来使用的,在controller中使用的话,是这么使用:$scope.$eval() 以上这篇angularjs中的$eval方

  • 对angularjs框架下controller间的传值方法详解

    AngularJS中的controller是个函数,用来向视图的作用域($scope)添加额外的功能,我们用它来给作用域对象设置初始状态,并添加自定义行为. 当我们在创建新的控制器时,angularJS会帮我们生成并传递一个新的$scope对象给这个controller,在angularJS应用的中的任何一个部分,都有父级作用域的存在,顶级就是ng-app所在的层级,它的父级作用域就是$rootScope. 每个$scope的$root指向$rootScope, $cope.$parent指向父

  • Angular中的$watch方法详解

    在$apply方法中提到过脏检查,首先apply方法会触发evel方法,当evel方法解析成功后,会去触发digest方法,digest方法会触发watch方法. (1)$watch简介 在digest执行时,如果watch观察的的value与上一次执行时不一样时,就会被触发. AngularJS内部的watch实现了页面随model的及时更新. $watch方法在用的时候主要是手动的监听一个对象,但对象发生变化时触发某个事件. (2)watch方法用法 $watch(watchFn,watch

  • Angularjs自定义指令Directive详解

    今天学习angularjs自定义指令Directive. Directive是一个非常棒的功能.可以实现我们自义的的功能方法. 下面的例子是演示用户在文本框输入的帐号是否为管理员的帐号"Admin". 在网页上放一个文本框和一个铵钮: <form id="form1" name="form1" ng-app="app" ng-controller="ctrl" novalidate> <i

随机推荐