var聲明變量:
var只有函數作用域,沒有塊級作用域
//函數作用域的表現 function test(){ var i =10; console.log(i); //10 } test(); //10 console.log(i); // i is not defined; //塊級作用域對var沒有約束 { var i = 10; console.log(i); //10 } console.log(i); //10
從上面的代碼可了解到,塊級作用域對var是沒有約束作用的。
let聲明變量:
let與var不同,let是有塊級作用域的。
//塊級作用域對let聲明的變量有約束 { let i = 10; console.log(i); //10 } console.log(i) //ReferenceError
了解了上面的特性再來看看,var和let在for循環的一些不同表現:
//var聲明i function test(){ for(var i=0;i<2;i++){ setTimeout(()=>{ console.log(i); },0); } } test(); //輸出結果:2、2 //let聲明i function test(){ for(let i=0;i<2;i++){ setTimeout(()=>{ console.log(i); },0); } } test(); //輸出結果:0、1
可以看到只是聲明方式不一樣,輸出的結果卻有很大的差異。
在此之前還需要了解setTimeout()的執行機制:
setTimeout()是以異步的方式執行的。在執行for循環的時候,並不是執行一次for循環就立刻執行一次setTimeout(),而會讓setTimeout()進入另一條線程進行等待,當主線程(這里就是test())執行完后,setTimeout()再依次執行。
在var中執行的時候:
因為var是沒有塊級作用域的,所以在for循環中聲明的i會存在於test()函數作用域中。每一次for循環就會聲明一次i,但后面聲明的變量會覆蓋前面聲明的變量。所以當for循環執行完后(此時setTimeout()還未被執行),函數作用域中的i的值就變成2了
而setTimeout()所在的線程中是這樣的:
//第一次進棧 setTimeout(()=>{ console.log(i); }); //第二次進棧 setTimeout(()=>{ console.log(i); });
這里的i都指向函數作用域中的i。所以輸出都為2。
在let中執行的時候:
因為塊級作用域的原因,let聲明的i都會存在於for塊級作用域中,每一次for循環都會生成一個塊級作用域。所以setTimeout()在線程中是這樣的:
{ let i=0; setTimeout(()=>{ console.log(i); }); } { let i=1; setTimeout(()=>{ console.log(i); }); }
所以會一次輸出0,1;