[面試專題]從for迴圈看let和var的區別

逺方小鎭發表於2019-02-16

從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
}

執行過程:

  1. 找到所有用 function 宣告的變數,在環境中「建立」這些變數。

  2. 將這些變數「初始化」並「賦值」為 function(){//具體內容}

  3. 執行程式碼,即fn()

  4. 函式體內找到所有用var宣告的變數,在環境中「建立」這些變數

  5. 將這些變數「初始化」為 undefined。

  6. 開始執行程式碼,x = 1 將 x 變數「賦值」為 1

  7. 列印x = 1 ,而y還未賦值,因此為undefined
    如果var改為let:

  8. 找到所有用 function 宣告的變數,在環境中「建立」這些變數。

  9. 將這些變數「初始化」並「賦值」為 function(){//具體內容}

  10. 執行程式碼,即fn()

  11. 函式體內找到所有用let宣告的變數,在環境中「建立」這些變數

  12. 開始執行程式碼,x = 1 將 x 變數「賦值」為 1

  13. 列印 x = 1 ,而y還未「初始化」,因此Uncaught ReferenceError.這就是所謂暫時性死區.

相關文章