浏览器事件发生时,会在浏览器的上下文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. mybatis-resultMap使用与详解

    1,当数据库的字段名与属性名称不一致时,在mybatis中如何处理? 第一种方式: 采用投影对字段重命名<select id="load" parameterType=&qu ...

  2. 【二】刚学Python的几道简单练习题

    python交友娱乐会所:613176398 1.使用while循环输入 1 2 3 4 5 6     8 9 10 2.求1-100的所有数的和 3.输出 1-100 内的所有奇数 4.输出 1- ...

  3. mapreduce新旧api对比

    对比:hadoop版本1.x 新版,hadoop版本0.x 旧版 1.新api引用包一般是mapreduce ,旧版api引用的包一般是mapred 2.新api使用Job,旧版api使用JobCon ...

  4. javascript DOM事件总结

    1 <html> 2 <title>事件</title> 3 <meta charset="utf-8"/> 4 <body& ...

  5. 初次就这么给了你(Django-rest-framework)

    Django-Rest-Framework Django-Rest框架是构建Web API强大而灵活的工具包. 简单粗暴,直奔主题. pip install django pip install dj ...

  6. NOIP算法总结与复习

    NOIP算法总结与复习 (看了看李总的蓝皮书,收获颇多,记下此文,以明志--) (一)数论 1.最大公约数,最小公倍数 2.筛法球素数 3.mod规律公式 4.排列组合数,错排 5.Catalan数 ...

  7. 你真的会阅读Java的异常信息吗?

    给出如下异常信息: java.lang.RuntimeException: level 2 exception at com.msh.demo.exceptionStack.Test.fun2(Tes ...

  8. Lowbit Sum 规律

    Lowbit Sum Time Limit: 2000/1000MS (Java/Others) Memory Limit: 128000/64000KB (Java/Others) SubmitSt ...

  9. Python in/not in --- if not/if + for...[if]...构建List+ python的else子句

    区分几个容易出错的地方: in 成员运算符 - 如果字符串中包含给定的字符返回 True >>>"H" in a True not in 成员运算符 - 如果字符 ...

  10. 用java编写一个微博登陆页面

    上次也写了一个微博登陆页面,不过功能还不够完善.今天重新完善了一些功能,分享出来给大家. 基本功能如下: (1)具有类似新浪微博的用户注册图形界面. (2)使用用户名或手机号注册,注册时需要提供新密码 ...