以昨天最後說的規則加入執行環境的角度來重新理解
小練習程式碼
var a = 1;
function test(){
  console.log("1.", a);     // undefined
  var a = 7;
  console.log("2.", a);     // 7
  a++;
  var a;
  inner();
  console.log("4.", a);     // 30
  function inner(){
    console.log("3.", a);   // 8
    a = 30;
    b = 200;
  }
}
test();
console.log("5.", a);       // 1
a = 70;
console.log("6.", a);       // 70
console.log("7.", b);       // 200
執行環境 (放一張有行數的原始碼圖片)
執行順序 (解說字的顏色對應到執行環境圖的配色)
let 與 const 的 Hoisting (以let舉例)
console.log(a)
let a = 10    //ReferenceError: Cannot access 'a' before initialization
上面看起來let沒有Hoisting,再加個function看看
let a = 10
function test() {
  console.log(a)
  let a = 30  //ReferenceError: Cannot access 'a' before initialization at test
}
test()
也一樣出現報錯,根據昨天理解的規則來看:沒有Hoisting表示沒有在function test()對a初始化,那不就應該要根據scope chain 往上層找到global的 a=10 來做印出嗎?但還是出現報錯。
所以let還是有提升,只是提升的方式和var不同。
TDZ:Temporal Dead Zone
let不會將變數先做初始化,所以在賦值之前都不能做存取它這件事,不然就會報錯。而在存取它到賦值的這段期間就稱做是TDZ(是一個為了解釋let與 const的Hoisting行為所提出的一個名詞。)
根據昨天的Hoisting作法,可以想成下面這樣:
let a = 10
function test() {
  let a            
  //在進到test()執行環境一樣有Hoisting,只是沒有像var將變數初始化undefined。
  .
  .
  .
  console.log(a)  //報錯 -> 在TDZ這個期間要存取還沒被賦值的a,就會報錯。
  .
  .
  .
  a = 30  
  //從進入test()執行環境開始 到出現賦值以前都是TDZ。
}
test()
TDZ是一種期間(不是空間):從進到function test()執行環境 到let a = 30賦值之前,都是TDZ 不能做存取,不然會報錯。中間可能有其他code會執行(上述程式碼以...代替),到執行a = 30賦值這ㄧ行,所以才會說是時間。