关于for循环中使用setTimeout

前言

       今天本来打算学习es6,结果刚开始就遇到一个问题,关于for循环中使用 var 和 使用 let 所带来的差别,我为了详细弄懂其中的原理,就查资料,查到了很多关于这方面的知识,这次就来记录一下。

在for循环中使用setTimeout的问题

有时候我们会有这样的需求,就是在for循环中使用到setTimeout,但是不一定能够得到我们想要的结果,看这个例子:

1
2
3
4
5
for (var i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, 1000)
}

结果是10次10.

其实在这种情况下我想要的情况应该是输出从0-9,而不是十次10。

那么为什么会这样呢,我是这样理解的:

  • 首先 setTimeout 是异步执行的并且先要到任务队列中才会到执行栈中,所以 setTimeout 的回调一定是最后执行的。
  • 在执行回调的时候会先在函数的局部作用域中去找 i 但是很显然是找不到的,然后会去全局作用域中找 i,这时就能找到,但是此时的i已经是10了,所以会输出10次10。

解决这个问题的多种方式

使用 let

在es6中可以使用let来声明块级变量,并且只在块级作用域中有效。来看看是如何解决这个问题的:

1
2
3
4
5
for (let i = 0; i < 10; i++) {
setTimeout(function () {
console.log(i);
}, 1000)
}

控制台打印结果0-9

可以看出其实使用let很轻松的就将问题解决了,那么这是如何解决的呢?
我是这样理解的:

  • 首先 setTimeout 是异步执行的并且先要到任务队列中才会到执行栈中,所以 setTimeout 的回调一定是最后执行的。这些都是不变的。

  • 但是区别在于由于使用了let来声明变量,所以在会在块级作用域中去找i,并且因为在每一次循环时都创建了一个不同的块级作用域(变量i不同),所以能够找到,并且按照顺序将i的值输出出来。

使用闭包

通过闭包也可以解决问题:

1
2
3
4
5
6
7
for (var i = 0; i < 10; i++) {
(function (i) {
setTimeout(function () {
console.log(i);
}, 1000)
})(i);
}

控制台打印结果0-9

那么闭包是如何解决的呢?
这是我的理解:

  • 首先 setTimeout 是异步执行的并且先要到任务队列中才会到执行栈中,所以 setTimeout 的回调一定是最后执行的。这些都是不变的。

+在执行匿名函数的时候形成了闭包,会保持对i的引用,就导致在每次循环的过程中匿名函数的作用域中都会有当前i的值,所以当后面执行回调函数的时候就能够在匿名函数的作用域中找到对应的i的值。

总结

这两天一直在看这方面的文章,感觉弄懂了很多这些方面的知识,继续加油。

-------------本文结束感谢您的阅读-------------