今天这篇文章是我在看前端一道比较经典的面试题所引起的思考。
首先让我们来看看这道题目:
for (var i = 0; i <= 5; i++) {
setTimeout(() => {
console.log(i)
}, i * 1000);
console.log('定时器外' + i)
}
如果你认为上面会打印出先打印0-5然后再打印定时器外0-5的话,那么恭喜你答错了,恰恰相反上面这行代码打印结果是,定时器外0~5 然后再打印6个6
这是为什么呢,首先因为在这段代码当中for循环是同步操作,而setTimeout是异步操作
在JavaScript里面会将同步操作进行先执行,让异步操作进入等待队列,当同步操作执行完成以后通知等待队列中的异步操作,然后系统安排等待队列中的定时器进行执行,而这个时候由于var它是没有作用域的,当同步操作执行完成过后i已经变成了5,而当定时器再来进行i的打印时,会打印出来6个6。
解决方案我觉得可以有四种
第一种采用ES6中的let方法:
因为let它拥有着函数的作用域,在for循环的时候用let的时候这时因为有自己的作用域范围,作用域范围内,有着属于自己的i而不是共用的i那么它打印出来的时候就是属于自己的i,结果为0~5
方法一
for (let i = 0; i <= 5; i++) {
setTimeout(() => {
console.log(i)
}, i * 1000);
}
第二种方法采用闭包的方法
这种方法的原理是因为在JavaScript里面只有function有着作用域的概念,那么当我们采用闭包的手法将i传入到一个立即执行的function,它打印出来的i就是当时传入的i,也就是自己作用域里面的i,
方法二
for (var i = 0; i <= 5; i++) {
setTimeout(() => {
(function (j) {
console.log(j)
})(i)
}, i * 1000);
}
方法三
通过分块的方式进行获取i,原理同样也是利用函数拥有作用域的方法,先将i保存到里面,然后在同步操作执行完成过后,异步中定时器的i已经有了自己的值,所以打印出来的