今天在寫代碼時遇到了個奇怪的問題。
for(var i = 0; i < 10; i++){
setTimeout(function() {
console.log(i)
}, 1000);
}
輸出為
10
10
10
10
...
這是個在JavaScript中十分常見的異步問題,出現(xiàn)這種輸出的根本原因在于:
for(var i = 0 ; i<10;i++)
中var i實際上是將i掛載到了全局變量上,當settimeout的回調(diào)函數(shù)執(zhí)行時,i已經(jīng)被for循環(huán)累加到了10,讀取并輸出的自然就是十個10了。
如果想依次輸出0~9該如何呢?在es5之前,有個取巧的方法,就是將i包裹在一個閉包中,將i作為一個函數(shù)的私有變量保存起來,這樣每次settimeout的回調(diào)函數(shù)就可以輸出屬于自己的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的聲明方式輸出結(jié)果就會截然不同了呢?
那就要從var與let的不同點說起。
let 與 var 最大的區(qū)別就在于,var 與let 生命的變量的作用域不同。
var 是以函數(shù)作為自己的作用域。
而 let 則是以{ }為作用域。
具體是什么意思呢?就以上邊的代碼為例,for循環(huán)展開后等價于
{
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循環(huán)一共循環(huán)幾遍,就會有幾個{},每一個{}都會有自己獨有的 i 。
而var則是以函數(shù)為單位,如果沒有外層函數(shù)就會掛載到全局變量上,所有的{}都會共有1個i,自然就會輸出同樣的值了。