实现异步循环
让我们试着写一个异步方法,每秒打印一次循环的索引值。
for (var i=0; i<5; i++) {
setTimeout(function(){
console.log(i);
}, 1000 * (i+1));
}
如上程序的输出为:
> 5
> 5
> 5
> 5
> 5
这明显是有问题的。
原因
每次时间结束(timeout)都指向原始的i
,而并非它的拷贝。所以,for循环使i
增长到5,之后timeout
运行并调用了当前i
的值(也就是5)。
好吧,这个问题看起来很简单。最直接的解决方法是将循环的索引缓存在一个临时变量里。
for (var i=0; i<5; i++) {
var temp = i;
setTimeout(function(){
console.log(temp);
}, 1000 * (i+1));
}
但是再次运行,如上的程序输出为:
> 4
> 4
> 4
> 4
> 4
这仍然有问题,这是因为并不存在块作用域,而且变量的声明被提升到了作用域顶端。实际上,如上代码和下面是一样的:
var temp;
for (var i=0; i<5; i++) {
temp = i;
setTimeout(function(){
console.log(temp);
}, 1000 * (i+1));
}
解决方法
有几个不同的方式可以拷贝i
。最普通且常用方法是通过声明函数来建立一个闭包,并将i
传给此函数。我们这里使用了自调用函数。
for (var i=0; i<5; i++) {
(function(num){
setTimeout(function(){
console.log(num);
}, 1000 * (i+1));
})(i);
}
在JavaScript里,参数是按值传递给函数的。像Number
、Date
和String
这些原始类型为基本复制。当你们在一个函数内改变它的值,并不影响外面的作用域。但Object
类型不一样:如果你在函数内部修改了它的参数,将会影响到所有包含该Object
的作用域内它的参数。
另一种方法是使用let
。在ES6中的let
关键字是可以实现的,它和var
不一样,因为它支持块作用域的。
for (let i=0; i<5; i++) {
var temp = i;
setTimeout(function(){
console.log(i);
}, 1000 * (i+1));
}
Use the 100 answers in this short book to boost your confidence and skills to ace the interviews at your favorite companies like Twitter, Google and Netflix.
GET THE BOOK NOWA short book with 100 answers designed to boost your knowledge and help you ace the technical interview within a few days.
GET THE BOOK NOW