浏览器事件发生时,会在浏览器的上下文window中执行,而angular有自己的上下文angular content,angular 事件在自己的上下文angular content中执行。

$watch

$watch组成了双向绑定的一边,在指定的表达式改变时调用,每个作用域都维护了一个监视器列表,被称为$scope.$$watchers。每次你绑定一些东西到你的UI上时你就会往$watch队列里插入一条$watch。

<ul>
<li ng-repeat="person in people">
{{person.name}} - {{person.age}}
</li>
</ul> app.controller('MainCtrl', function($scope) {
$scope.people = [
{name: 'a', age: 21},
{name: 'b', age: 25},
{name: 'c', age: 22}
];
});

这个例子生成了多少个$watch呢?ng-repeat一个,每个person两个,一共(2*3)+1=7个。

除了在视图上绑定数据创建一个$watch,还可以自定义$watch,但是有几点需要注意。

<body ng-controller="MainCtrl">
<input ng-model="user.name" />
Name updated: {{updated}} times.
</body> app.controller('MainCtrl', function($scope) {
$scope.user = { name: "Fox" }; $scope.updated = 0; $scope.$watch('user', function(newValue, oldValue) {
if (newValue === oldValue) { return; } //防止初始化时调用
$scope.updated++;
}, true); //第三个参数可选,如果设为true,则会比较对象的值,否则比较引用
});

当controller执行到这个$watch时,它会立即执行一次,所以我们加入了判断来防止第一个初始化的调用。其次是第三个参数,如果省略或者为false,则以上例子输入框的值改变之后,update的值不会改变,因为比较的user的引用。

每一个绑定到了UI上的数据都会生成一个$watch,那这些$watch是什么时候生成的呢?当我们的模版加载完毕时,也就是在linking阶段(Angular分为compile阶段和linking阶段),Angular解释器会寻找每个directive,然后生成每个需要的$watch。

$digest

$digest是将$watch和$apply绑定在一起的魔法胶水函数。从高级别来看,$digest将计算作用域(及作用域的孩子)中所有的$watch表达式。并在任何一个发生改变时调用监视器回调。但是监视器也可以改变作用域,所以其他监视器需要得到这个通知,因此$digest发生在一个循环中,如下面的伪代码所示:
var dirty = true;
var iterations = 0;
while (dirty && iterations++ < TIMES_TO_LOOP) {
dirty = false;
for (var i = 0; i < scope.watchers.length(); i++) {
var currentValue = scope.watchers[i].get();
if (currentValue != scope.watchers[i].oldValue) {
dirty = ture; //如果有数据改变了,则又$digest一次,直到没有数据改变
scope.watchers[i].callback(currentValue, scope.watchers[i].oldValue);
scope.watchers[i].oldValue = currentValue;
}
}
}

angular将TIMES_TO_LOOP(TTL)默认设为10,即$digest最多循环10次,超出后将抛出一个错误,防止无限循环,我们可以通过$rootScope服务和digestTtl函数改变这个值。

var app = angular.module('MyApp', [], function ($rootScopeProvider) {
$rootScopeProvider.digestTtl(15); //将TTL改为15
});

angular的内置指令会默认调用$digest,还有一点需要注意的是,UI中的所有指令都将创建至少一个监视器,所以angular的性能问题几乎总是因为以不合理的方式使用ngRepeat指令所引起的。如以上第一段代码,如果person对象达到10000个以上就会严重影响性能,实际上angular建议单个页面中不要使用超过2000个监视器。


除了默认调用之外,有时候我们也可以手动调用$digest循环,记住只有angular事件,也即是angular content处理的事件才会调用$digest循环,如果在window环境处理的事件,比如setTimeout,Jquery事件,则不会调用$digest循环,如果我们不手动调用,则视图不会随模型数据变化更新。
angular.module('app', function ($scope) {
setTimeout(function () {
$scope.arr = [1];
$scope.$digest(); //setTimeout中手动调用$digest
});
});

$apply

$appy组成了双向数据绑定的另一边,它将通知angular某些东西改变了,$watch表达式的值应该重新计算。实际上$apply总是会默认调用$digest循环。它的官方伪代码如下:
function $apply(expr) {
try {
return$eval(expr);
} catch(e) {
$exceptionHandler(e);
} finally {
$root.$digest();
}
}

事件发生时是谁在决定事件是在window中执行还是angular content执行?答案是$apply(),例如内置的ngClick指令会默认调用$apply。那么什么时候需要我们手动调用$apply呢?和$digest一样,在angular里面执行angular框架之外的表达式,比如说:浏览器DOM事件、setTimeout、XHR或其他第三方的库时,这时就要求我们手动调用$apply()。


我们可以把函数传入$apply中,也可以在外部单独调用,但是有一些区别。
element.bind('click', function() {
scope.foo++;
scope.bar++;
scope.$apply(); //我们是在angular context的外面更新的数据,
//如果有发生错误,Angular永远不知道
}); element.bind('click', function() {
scope.$apply(function() { //把它封装进$apply里面,错误会传到angular里
scope.foo++; //例如一个服务器获取数据失败
scope.bar++;
});
})

angular核心$watch,$digest,$apply之间的联系的更多相关文章

  1. 谈谈Angular关于$watch,$apply 以及 $digest的工作原理

    这篇文章主要是面向那些刚开始学AngularJs和想要了解数据绑定(data-binding)是怎么工作的, 如果你已经熟悉如何使用angularjs了,我强烈建议你不用阅读了. angularjs使 ...

  2. Angular:手动脏检查/$apply/$digest和监控对象/$watch

    声明:借鉴好多chm资料.视频.PDF总结如下: 一.$apply的引入 View <div ng-app=""> <div ng-controller=&quo ...

  3. angular : direative :comunication 指令之间的通讯

    在网络上可以找到多种指令之间的通讯 · $on,$emit,$boardcast (向上或向下冒泡) · 指令return的required (^)向上一个scope通讯,前提要先给scope一个na ...

  4. Angular学习笔记之组件之间的交互

    1.@Input:可设置属性 当它通过属性绑定的形式被绑定时,值会“流入”这个属性. 在子组件中使用,例如:@Input()name:string 父组件定义宾亮,并在父组件的模板中绑定,例如: 子组 ...

  5. angular中对象与字符串之间的转换

    1.angular 里 字符串与对象互转  angular.toJson();将字符串转成对象 angular.forJson(); 将字符串转成对象  2.angular 循环    <scr ...

  6. Angular 学习笔记——自定义指令之间的交互

    <!DOCTYPE html> <html lang="en" ng-app="myApp"> <head> <met ...

  7. Vue与Angular以及React的三者之间的区别

    1.与AngularJS的区别 相同点:都支持指令:内置指令和自定义指令:都支持过滤器:内置过滤器和自定义过滤器:都支持双向数据绑定:都不支持低端浏览器. 不同点:AngularJS的学习成本高,比如 ...

  8. Angular DirtyChecking(脏值检查) $watch, $apply, $digest

    Dirty Checking (脏值检查) Digest cycle and $scope Digest cycle and $scope First and foremost, AngularJS ...

  9. angular $apply()以及$digest()讲解1

    一些知名的批评和缺陷.他们都涉及到$digest loop(更新周期)中一个很常见的问题:如何在Angular之外更新$scope? 在哪调用 $apply? 更佳的做法是确保你是在$digest l ...

随机推荐

  1. cnblogs第一天

    2017-08-25 21:27:16 今天算是真的下定决心要好好的去经营自己的博客了. flag也就不立了,毕竟,flag这种东西立了就是为了打脸的嘛....... 既然说是经营了,那必然是要认认真 ...

  2. win7系统Myeclipse下切换SVN用户

     Eclipse的SVN插件Subclipse做得很好,在svn操作方面提供了很强大丰富的功能.但到目前为止,该插件对svn用户的概念极为淡薄,不但不能方便地切换用户,而且一旦用户的帐号.密码保存之后 ...

  3. shell脚本获取文件中key/value的小方法

    方法有N种,awk.sad.grep.cut... 以上几种方式不写了,就写两个不太常用到的. 废话少说,直接上代码: cat a.txt aa.gif=aaaa.gif bb.gif=bbbb.gi ...

  4. JVM读书笔记PART3

    一.早期(编译器)优化 语法糖 c#和java的泛型截然不同看似相同,c#是真实的泛型 编译运行一直存在 List<string> 和List<int> 就完全是两个类 而Ja ...

  5. [UWP]创建一个进度按钮

    1. 前言 最近想要一个进度按钮. 传统上UWP上处理进度可以这样实现,首先是XAML,包括一个ProgressBar和一个按钮: <StackPanel Orientation="H ...

  6. 初始Socket编程(python)

    通信双方要有一个服务端和一个客户端,所以要分开去写代码. 所以我创建了两个py程序,第一个是服务端:iServer.py 和客户端 iClient.py 服务端: #coding:utf-8from ...

  7. 【POJ】2348 Euclid's Game(扩欧)

    Description Two players, Stan and Ollie, play, starting with two natural numbers. Stan, the first pl ...

  8. Gaussian and Truncated Gaussian

    Everybody knows about Gaussian distribution, and Gaussian is very popular in Bayesian world and even ...

  9. Django进阶篇【2】

    在学习之前,我们补充一个知识点(static用法) 创建APP 配置: setting.py STATICFILES_DIRS = ( os.path.join(BASE_DIR,'static') ...

  10. Gate One——用web展示Terminal(安装)

    Gate One可以用web来展示Terminal,虽然存在一些小缺陷,基本功能都还可以的,有兴趣的可以折腾一下. 安装环境: 系统:RHEL 6.1 ,系统自带python 2.6.6 下载需要安装 ...