for循环与setTimeout结合面试问题巩固
先来一个经典的面试题
for(var i = 0; i < 5; i += 1)
{
setTimeout(function() {
console.log(i)
}, 1000)
}
最终的结果会是多少?
一秒后...
5
5
5
5
5
问题
1. 为什么输出的是5
2. 为什么输出的全部都是5
3. 为什么定时器是一秒后全部都输出,而不是每过一秒钟输出一个
解答
1. 为什么是5
因为使用的是var 全局变量,当for循环运行到最后一次的时候 4(i的值) += 1
,只不过这个没有通过for循环的判定条件结束了,但是 4+=1
却执行了 所以公共变量i的值是5
2. 为什么输出的全部都是5
因为javascript是单线程,所以有异步执行机制,先执行同步任务,后执行异步任务,for循环是同步任务,而setTimeout是异步任务,所以首先会执行for循环,当for循环执行完时i是5,这时候才开始执行异步任务,所以全部输出的都是5
3. 为什么定时器是一秒后全部输出,而不是每过一秒输出一个
当定时器回调时,是以执行的时间为起点经过设置的延迟时间后执行设置指令,for循环中执行的5次,但定时器的起点依然是第一次被回调的时间点,所以定时器是一秒后全部输出而不是没过一秒输出一次
这时候可能会问,有没有办法正确的输出i的值呢?
// 方法一
for(let i = 0; i < 5; i += 1)
{
setTimeout(function() {
console.log(i)
},1000)
}
// 方法二
for (var i = 0; i < 5; i++) {
setTimeout(
(function (i) {
return function () {
console.log(i);
};
})(i),
i * 1000
);
}
结果
// 一秒后...
0
1
2
3
4
问题
1. 为什么使用var就不行,而let就行呢?
2. 在立即执行函数中return函数的作用是什么?
答案
1. 为什么使用var就不行,而let就行呢?
因为let是块级作用域,所以每一次的console.log(i)都是i当前的值,在定时器未执行前都不会被释放
2. 在立即执行函数中return函数的作用是什么?
我们需要立即执行函数只是为了获取到for循环作用域内的i值,而setTimeout的第一个参数必须是需要编译的代码
或者一个函数方法
,此时就需要return一个函数方法并接收立即执行函数传值完成以上效果
当我们需要隔一秒钟输入一次呢?
for(var i = 0; i < 5; i += 1)
{
setTimeout(function() {
console.log(i)
}, i * 1000)
}
结果
5
// 一秒钟...
5
// 一秒钟...
5
// 一秒钟...
5
// 一秒钟...
5
问题解答
1. 为什么这样写会隔一秒输出一次?
在定时器第二个参数接收的i值为for循环作用域内的i值,当定时器开始执行时,是以执行时的时间为起点,根据第二参数设定的等待时间,进行执行, 所以随着i值变化也就是第二参数的变化进行定时器内函数的回调, 大致效果如下:
请1秒钟后console.log(i)
请2秒钟后console.log(i)
请3秒钟后console.log(i)
请4秒钟后console.log(i)
请5秒钟后console.log(i)
// 一秒钟后
5
// 两秒钟后
5
// 三秒钟后
5
// 四秒钟后
5
// 五秒钟后
5