for 循环中嵌套定时器问题
在学习js的时候,或者面试的时候,会经常碰到这一道经典题目:
for(var i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
});
}
console.log('a');
输出结果是:
a
5
5
5
5
5
结果是先打印字符串’a’,然后再打印5个数字5。
有人会说这个题目并不难,而且只要你遇到过这个题目,下次再见到基本也不会答错了,但其实这段简单的代码里面包含了很多js知识。
第一次看到这段代码的时候,会给人一种错觉:
会先打印for循环里面的5次i值,然后才会去打印下面的字符串’a’
for循环里面的打印结果会是0,1,2,3,4,而不是什么5个5这种奇怪的结果
但是实际运行结果跟我们预期的不一样,原因就是因为这里涉及到了js的运行机制。
js是单线程语言
js是单线程语言,牵扯到同步和异步的问题,js代码先执行同步,在执行异步。
这串代码先执行 for循环 和 输出a,最后在执行定时器。
所以实际循行的顺序是:
for(var i = 0; i < 5; i++) {
}
console.log('a');
setTimeout(function () {
console.log(i);
});
setTimeout(function () {
console.log(i);
});
setTimeout(function () {
console.log(i);
});
setTimeout(function () {
console.log(i);
});
setTimeout(function () {
console.log(i);
});
作用域和闭包
这道题目还会引申出来另一个问题:
如果想要for循环里的定时器打印出0,1,2,3,4,而不是5个5,该怎么办?
答案是:使用立即执行函数。
for(var i = 0; i < 5; i++) {
(function(i) {
setTimeout(function () {
console.log(i);
});
})(i)
}
console.log('a');
打印结果:
a
0
1
2
3
4
方法2:在Es6中的语法使用let关键字
将for循环中的var替换成let
for (let i = 0; i < 5; i++) {
setTimeout(function () {
console.log(i);
});
}
console.log('a');