Javascript按轮次执行
Javascript代码是按轮次执行的,从第一行开始一直到最后一行依次执行,正常情况下中间不会被中断,在代码执行的过程中浏览器不会有任何响应。
不过任何异步回调,用户的输入操作等会新起一轮,新起的一轮会加入到事件轮询队列,直到下个CPU空闲会被执行。
怎么实现双向数据
一般来说有两种比较流行的方法:
1.使用自定义的对象,这个对象的数据的更新需要用到自定义的set方法,而不是直接赋值。
这样做的好处是,每次数据的更新我们都可以知道,接下来利用通知就可以更新DOM了。缺点是必须使用类似于
obj.set('key', 'value')来设置属性的值,而不能直接用ojb.key='value'这种赋值方式。
EmberJS和KnockoutJS用的就是这种实现方式。
2.在每次执行轮次的结束阶段,检测变量的值是否发生了变化,然后再通知DOM进行更新。
AngularJS用的就是第二种方式来实现双向数据绑定的。
angular $apply and $digest
在Angular里完成数据检测的工作是由$scope.$digest()来完成的,负责检测绑定的数据是否发生了变化。一般情况下我们不会直接用到这个函数,
而是用$scope.$apply(),实际上$apply会帮我们调用$digest,只是他在外面包装了一层而已。
$apply([exp]);
$apply() is used to execute an expression in angular from outside of the angular framework. (For example from browser DOM events, setTimeout, XHR or third party libraries). Because we are calling into the angular framework we need to perform proper scope life cycle of exception handling, executing watches.
Life cycle
Pseudo-Code of $apply()
function $apply(expr) {
try {
return $eval(expr);
} catch (e) {
$exceptionHandler(e);
} finally {
$root.$digest();
}
}
Scope's $apply() method transitions through the following stages:
The expression is executed using the $eval() method.
Any exceptions from the execution of the expression are forwarded to the $exceptionHandler service.
The watch listeners are fired immediately after the expression was executed using the $digest() method.
Angular文档,$apply接收一个表达式字串,由$eval执行,并在执行后调用$digest方法。
一般情况下我们很少会用到$apply函数,因为实际上所有的ng指令都帮我们包装了一层$apply,比如ng-click,$http。所以很少会显式去调用这个方法。
而且在这些指令里面再调用会报错,在$apply函数里再次调用$apply会导致报错。
如果现在有部分代码你需要在新的执行轮次运行,并且不是由Angular的内部库创建的,那么这种情况会需要用到$apply函数。这样在你的代码执行后,
变量发生了改变就会被Angular检测到。
比如下面这段代码:
function Ctrl($scope) {
$scope.message = "Waiting for update";
setTimeout(function () {
$scope.message = "Timeout called!";
// AngularJS 不会意识到$scope发生了变化
}, 2000);
}
回调函数里改变了$scope.message的值,但这不会被Angular检测到(因为根本没有进行检查)。
所以我们需要手动调用$apply函数。
//use $apply
function testCtrl($scope) {
$scope.message = 'hello world';
setTimeout(function() {
$scope.$apply(function() {
$scope.message = 'msg update';
});
}, 1000);
}
//ng $timeout 已经帮我们包装了$apply,所以一般情况下直接用$timeout就可以
ng $watch
每次在DOM增加一个变量就相当往ng 的$watch list 里增加一个$watch item
每次end of turn都会检查$watch list的变量有没有变化
//注册自己的$watch,并添加$digest时有变化的回调方法
$scope.$watch('name', function(value) {
});
坑
如果自己写的组件上更新了变量值,但没有调用$apply,那么就不会检测到该变量的变化。
特别是某个地方用了$watch来检测某个值,然后这个值发生了改变,却没有执行变量值的检测,就会出现
变量值发生了改变但是$watch却不会被调用的情况。