js中for循环和定时器的问题,有四个解决方法
这里面涉及到了同步与异步的问题
也可以理解为:
解决方法1:闭包
解决方法2:拆分结构
解决方法3:let
let和var区别
解决方法4:第三个参数
for (var i = 0; i < 6; i++) {
setTimeout(() => {
console.log(i);
}, 1000);
}
1
2
3
4
5
如上代码,我们编写代码的思路是想要使 0 1 2 3 4 5间隔1秒陆续出现
但是真正的结果是连续输出6个6
这里面涉及到了同步与异步的问题
setTimeout是异步执行的,每一次执行一次for循环时,setTimeout都会执行一次,但是里面的函数却不会执行,而是被放在了任务对列里面,也就是说只有主线上的全部执行完才会执行任务队列里的任务 == for循环全部执行完毕后(此时 i=5),才开始执行fun函数,因此虽然定时器跑了6秒,但控制台上是6个6
也可以理解为:
(for循环非常的快,从开始到结束只需要几微妙或几毫秒,当定时器跑完一秒之后for循环早就全部执行完成)
解决方法1:闭包
for (var i = 0; i < 6; i++) {
(function(j) {
setTimeout(() => {
console.log(j);
}, j * 1000);
}(i));
// 代码格式
// (function(a) {})(b)
}
1
2
3
4
5
6
7
8
9
我们可以发现跟预期结果一致,依次输出1到5,因是因为实际参数跟定时器内部的i有强依赖。
通过闭包,将i的变量驻留在内存中,当输出j时,引用的是外部函数的变量值i,i的值是根据循环来的,执行setTimeout时已经确定了里面的的输出了。
解决方法2:拆分结构
function timeout(i) {
setTimeout(console.log(i), i * 1000);
}
for (var i = 0; i < 6; i++) {
timeout(i);
}
1
2
3
4
5
6
这个是在网上看到的,确实可以输出1到5,但似乎没有做到一秒一个,仅供参考
解决方法3:let
for (let i = 0; i < 6; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000)
}
1
2
3
4
5
这个强烈推荐,简单好记还有用!!!!!
只把var改为let,可却可以输出1到5,而且是一秒一个数,并不是按设定是时间执行的
因为for循环头部的let不仅将i绑定到for循环中,事实上它将其重新绑定到循环体的每一次迭代中,确保上一次迭代结束的值重新被赋值。setTimeout里面的function()属于一个新的域,通过var定义的变量是无法传入到这个函数执行域中的,通过使用let来声明块变量能作用于这个块,所以function就能使用i这个变量了;这个匿名函数的参数作用域和for参数的作用域不一样,是利用了这一点来完成的。这个匿名函数的作用域有点类似类的属性,是可以被内层方法使用的。
解决方法4:第三个参数
for (let i = 0; i < 6; i++) {
setTimeout(function() {
console.log(i);
}, i * 1000, i)
}
1
2
3
4
5
由于每次都是从for里面取到的值,所以会依次输出1到5,试了一下,这种方法最方便。