今天在写代码时遇到了个奇怪的问题。
for(var i = 0; i < 10; i++){
setTimeout(function() {
console.log(i)
}, 1000);
}
输出为
10
10
10
10
...
这是个在JavaScript中十分常见的异步问题,出现这种输出的根本原因在于:
for(var i = 0 ; i<10;i++)
中var i实际上是将i挂载到了全局变量上,当settimeout的回调函数执行时,i已经被for循环累加到了10,读取并输出的自然就是十个10了。
如果想依次输出0~9该如何呢?在es5之前,有个取巧的方法,就是将i包裹在一个闭包中,将i作为一个函数的私有变量保存起来,这样每次settimeout的回调函数就可以输出属于自己的i了。
但 let 指令可以帮助我们完美的解决这个问题,将上述代码做一下改造。
for(let i = 0; i < 10; i++){
setTimeout(function() {
console.log(i)
}, 1000);
}
可以看出,与最初的代码唯一的不同,就是将 var i= 0 改为了,let i = 0
测试一下,输出为
0
1
2
3
..
为什么改了i的声明方式输出结果就会截然不同了呢?
那就要从var与let的不同点说起。
let 与 var 最大的区别就在于,var 与let 生命的变量的作用域不同。
var 是以函数作为自己的作用域。
而 let 则是以{ }为作用域。
具体是什么意思呢?就以上边的代码为例,for循环展开后等价于
{
let i = 0;
setTimeout(function() {
console.log(i);
}, 1000);
}
{
let i = 1;
setTimeout(function() {
console.log(i);
}, 1000);
}
{
let i = 2;
setTimeout(function() {
console.log(i);
}, 1000);
}
...
而使用var时则是
var i = 0;
i++;
i++;
i++;
...
{
setTimeout(function() {
console.log(i);
}, 1000);
}
{
setTimeout(function() {
console.log(i);
}, 1000);
}
{
setTimeout(function() {
console.log(i);
}, 1000);
}
...
这么看是不是就直观了许多?使用let生命的变量,作用域只存在于一个{}之间,所以for循环一共循环几遍,就会有几个{},每一个{}都会有自己独有的 i 。
而var则是以函数为单位,如果没有外层函数就会挂载到全局变量上,所有的{}都会共有1个i,自然就会输出同样的值了。