從for迴圈看let和var的區別
MDN的let:
let允許你宣告一個作用域被限制在塊級中的變數、語句或者表示式。與var關鍵字不同的是,var宣告的變數只能是全域性或者整個函式塊的。在 ECMAScript 2015 中,let 繫結不受變數提升的約束,這意味著 let 宣告不會被提升到當前執行上下文的頂部。在塊中的變數初始化之前,引用它將會導致 ReferenceError(而使用 var 宣告變數則恰恰相反,該變數的值是 undefined )。這個變數處於從塊開始到 let 初始化處理的”暫存死區“之中。
迴圈定義中的let作用域:迴圈體中是可以引用在for宣告時用let定義的變數,儘管let不是出現在大括號之間.
var list = document.getElementById("list");
for (let i = 1; i <= 5; i++) {
var item = document.createElement("LI");
item.appendChild(document.createTextNode("Item " + i));
let j = i;
item.onclick = function (ev) {
console.log("Item " + j + " is clicked.");
};
list.appendChild(item);
}
在每次迴圈的時候用 let j 保留的 i 的值,所以在 i 變化的時候,j 並不會變化。而console.log 的是 j,即使此處i宣告換成var,結果依然一樣.
MDN此處的寫法是多餘的,去掉let j = i依然可以實現上述效果,只是方便理解let.事實上去掉let j = i,等效於:
for (let i = 1; i <= 5; i++) {
//let i = i;即將i的作用域放在了改塊級作用域中
var item = document.createElement("LI");
item.appendChild(document.createTextNode("Item " + i));
item.onclick = function (ev) {
console.log("Item " + i + " is clicked.");
};
list.appendChild(item);
}
console.log(i)// Uncaught ReferenceError: i is not defined
上邊程式碼中最後的ReferenceError表明,i雖然在for迴圈{}的外層,但是實際上是被繫結在該塊級作用域內的.這裡和var是不一樣的,var宣告的i在外層,因此成為全域性變數.
變數提升(hoist)
let x = "global";
(function() {
console.log(x); // Uncaught ReferenceError: x is not defined
let x = `part`;
console.log(x)
}());
眾所周知,let變數沒有hoist,按這個理解,函式內第一行log(x)應當是global才對,但是卻報錯了.去掉let x = `part`就沒有問題,這說明後邊的宣告影響到了第一行的log(x).
fn();
function fn(){
var x = 1;
console.log(x,y)
var y = 2
}
執行過程:
-
找到所有用 function 宣告的變數,在環境中「建立」這些變數。
-
將這些變數「初始化」並「賦值」為 function(){//具體內容}
-
執行程式碼,即fn()
-
函式體內找到所有用var宣告的變數,在環境中「建立」這些變數
-
將這些變數「初始化」為 undefined。
-
開始執行程式碼,x = 1 將 x 變數「賦值」為 1
-
列印x = 1 ,而y還未賦值,因此為undefined
如果var改為let: -
找到所有用 function 宣告的變數,在環境中「建立」這些變數。
-
將這些變數「初始化」並「賦值」為 function(){//具體內容}
-
執行程式碼,即fn()
-
函式體內找到所有用let宣告的變數,在環境中「建立」這些變數
-
開始執行程式碼,x = 1 將 x 變數「賦值」為 1
-
列印 x = 1 ,而y還未「初始化」,因此Uncaught ReferenceError.這就是所謂暫時性死區.