本博文是基于英文文章的中文翻译和简单解释,原文请看此处。
如果你使用过一段时间的angularJS,那么有极大的可能遇到过这样的代码 $scope.$apply. 表面上看来,这个代码是用来对绑定在view上的数据进行更新。那么什么时候需要用这种方式来更新绑定数据呢?为什么需要用这种方式呢?我们来一探究竟。
1.什么是javascript的多轮回(turns)机制
稍具功能的javascript代码,都不是一次就运行完成所有功能。相反的,不同的javascript方法很多时间是在不同的轮次里完成的(其实就是通常说的回调和事件轮询). 看个例子:
<pre name="code" class="javascript">var button = document.getElementById('clickMe');
function buttonClicked(){
alert('clicked');
}
button.addEventListener('click',buttonClicked);
function timerComplete(){
alert('hello');
}
setInterval(timerComplete,3000);
我们分析一下这个代码有几轮(turn)
第一轮:是这段第一次运行。 做些什么事呢?首先,找到ID为clickMe的DOM组件,为它安装监听事件。然后设置一个定时触发事件,每3秒调用一次timerComplete函数。本轮结束。
第二轮:当3秒的时候来到时,触发timerComplete函数。本轮结束。
第三轮: 当有用户点击clickMe时,触发监听事件。本轮结束。
第N轮: ...
2. 多轮回(turns)和angularJS有神马关系呢
多轮回和angularJS的双向绑定有着很密切的关系。比如说,每当model数据改变时,view中相应的绑定显示也会改变,但是angularJS是怎么知道的呢?
其实很简单。angularJS会在任何一轮javascript运行结束时候,去看一下这些被绑定的数据是否发生了变化,如果绑定数据变化了,那么就去改变相应的view。
是不是很抽象? 我们举个栗子, 用angularJS写一个超简单的时钟程序:
首先是html部分
<!doctype html>
<html>
<head>
<script type="text/javascript" src="./js/angular.min.js"></script>
<script type="text/javascript" src='./js/main.js'></script>
</head>
<body ng-app='App'>
<div ng-controller='PlayController'>
现在时间是 {{ clock }}
</div>
</body>
</html>
然后是我们的main.js
var app = angular.module('App',[]);
app.controller('PlayController',['$scope',function(){
var updateClock = function(){
$scope.clock = new Data();
};
setInterval(updateClock,1000);
}]);
运行一下,看看会发生什么?哈哈,什么也没有发生,时间一直停止。如果你打印出clock的话,你会在控制台看到每秒都有输出,但是我们的界面上就是没有改变。angularJS不是在每轮结束时都会去看看绑定数据有没有变化吗?那为什么现在会这样呢?
3. angularJS 数据绑定的前提: $scope.$apply
终于说到主题了。 原来angularJS并不是智能的去看我们的绑定数据,它需要我们用$scope.$apply来显示的告诉它。
那难道我必须在所有绑定数据的地方都调用$scope.$apply么?经验告诉我并不是这样啊。你得到它了!现实中,我们很少用到$scope.$apply。这是因为angularJS会帮我们完成这种转换。ng-click事件,$http回调等都会被自动转换。
当你需要在一个新的javascript轮回中调用一个方法,并且这个方法不是用angularJS库方法调用时,就需要使用$scope.$apply
所以,修改我们的main.js 如下:
var app = angular.module('App',[]);
app.controller('PlayController',['$scope',function(){
var updateClock = function(){
$scope.$apply(function(){
$scope.clock = new Data();
});
};
setInterval(updateClock,1000);
}]);
看看效果吧,时钟动起来了。
另一种方法,就是使用angularJS自带的$interval服务。这个就留个大家了。