今天在寫代碼時遇到了個奇怪的問題。
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實(shí)際上是將i掛載到了全局變量上鳄厌,當(dāng)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的不同點(diǎn)說起寞埠。
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)幾遍遣钳,就會有幾個{},每一個{}都會有自己獨(dú)有的 i 麦乞。
而var則是以函數(shù)為單位蕴茴,如果沒有外層函數(shù)就會掛載到全局變量上,所有的{}都會共有1個i姐直,自然就會輸出同樣的值了倦淀。