for循环中使用var出现的问题
首先for循环是同步执行的,如果内部写的是同步代码是不会遇到索引值错误的。
for (var i = 0; i < 5; i++) {
console.log(i);
}
一般来说问题出现考虑两点:
- var是函数作用域的。
- 事件循环中任务队列中的微任务和宏任务是当前事件循环的结束和下一次循环的开始执行的(当然最早的一次是宏任务,读取JavaScript代码)
for (var i = 0; i < 5; i++) {
console.log(i);
new Promise((resolve) => {
resolve(i);
}).then((result) => {
console.log(`res=${result}`);
});
setTimeout(() => {
console.log(i);
}, 500);
}
上面代码输出结果为 0~4, res=[0,4], 5个5,promise为微任务,setTimeout为宏任务。
这里就解释一下为什么setTimeout是5个5,但Promise输出的却是对的,这两个的行为其实类似,都是for循环结束后,才轮到他们执行,由于var的作用域不是块级的,for循环结束后var已经变成了5,因此setTimeout每次输出的值都是5,而Promise的值为什么是正确的如下代码,相信应该都看过。
for (var i = 0; i < 5; ++i) {
((x) => {
setTimeout(() => {
console.log(x);
}, 500);
})(i);
}
解释性的说法是:用立即执行函数并绑定当前传入这个闭包参数 i,因此即使是后面执行但每轮循环绑定的值已经不一样了。
最主要的应该是,这段代码的外层是同步代码,就和Promise的构造函数是同步代码一样,他们是立即执行的,因此Promise构造函数中的resolve的i是每次执行的而不是最后执行,then则是在setTimeout前执行。
let
再说一下let,事实上let所做到的就是将其重新绑定到了循环的每一个迭代中,确保使用上一个循环迭代结束时的值重新进行赋值。 一方面let也是块级作用域的。
var作为函数作用域常常污染外部变量,如下代码就是证明,最终输出结果是5个30。
for (var i = 0; i < 5; ++i) {
setTimeout(() => {
console.log(i);
}, 500);
}
i = 30;