以昨天最後說的規則加入執行環境的角度來重新理解
小練習程式碼
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
賦值這ㄧ行,所以才會說是時間。