暫時性死區的表現
if (true) {
console.log(value);//VM58:2 Uncaught ReferenceError: Cannot access 'value' before initialization
let value = 1;
}
進入當前作用域,在變量聲明之前訪問變量,是無法訪問到的。
這是由於let/const沒有變量提升(提升到作用域頂部),因此通過let/const定義的變量不會被提升到作用域頂部——也就是此時的塊級作用域,因此在聲明之前無法訪問。
但是為什么報錯信息是“Cannot access 'value' before initialization”,而不是我們常見的“value is not defined”呢,這2者有啥區別?
比如以下代碼,由於塊級作用域,外部是沒有value聲明的,所以會報錯“value is not defined”
if (false) {
let value = 1;
}
console.log(value);//Uncaught ReferenceError: value is not defined
原因排查
我們通過控制台的作用域來看看
if (true) {
debugger
let value = 1;
}
從上圖可以看到,控制台的Block作用域里value已經存在了,說明value肯定是被定義了,因此肯定不會報錯“value is not defined”,但是又不法訪問,原因是當前時刻為“before initialization”。
那也就是“defined”和“initialization”是有區別的?
我這樣理解
為了理解以上現象,
參考文章我用了兩個月的時間才理解 let,我們可以把 JS 變量分為「創建create、初始化initialize 和賦值assign」3個步驟。
var 聲明的「創建、初始化和賦值」過程
if (true) {
console.log(x, y) // undefined,undefined
var x = 1
var y = 2
}
執行上述代碼時,會有如下步驟:
- 找到代碼塊中所有用 var 聲明的變量,在這個環境中「創建」這些變量(即 x 和 y)。
- 將這些變量「初始化」為 undefined。
- 開始執行代碼
x = 1 將 x 變量「賦值」為 1
y = 2 將 y 變量「賦值」為 2
也就是var在執行賦值操作之前,就將「創建變量,並將其初始化為 undefined」。因此通過var聲明變量之前,在同一作用域下訪問變量,得到的是undefined。
let 聲明的「創建、初始化和賦值」過程
if (true) {
console.log(x, y) // Cannot access 'value' before initialization
let x = 1
let y = 2
}
- 找到所有用 let 聲明的變量,在環境中「創建」這些變量
- 開始執行代碼(注意現在還沒有初始化)
- 執行let x = 1,將 x 「初始化」,並「賦值」為 1(let x 實現初始化,x = 1實現賦值)
- 對let y = 2實現相同的步驟
無變量提升
從以上分析來看,
- 我們平常所說的“變量提升“其實是指將「創建」和「初始化」這2個步驟都提升了
- var存在變量提升,因為其同時提升了「創建」和「初始化」
- let/const不存在變量提升,實際上是因為let/const只提升了「創建」,而沒有提升「初始化」。
同時,上面的報錯也很好理解了:
- “value is not defined”是因為變量沒有「創建」
- “Cannot access 'value' before initialization”是「創建」了變量,但沒「初始化」
因此,所謂暫時性死區,就是不能在初始化之前使用變量。
需要暫時性死區的原因
ES6增加暫時性死區這一特性,主要是為了減少運行時錯誤,防止聲明之前就使用。
但是為什么不直接將「創建」過程也不提升呢?
我的理解是由於js是靜態作用域,在代碼編譯的時候就會去分析各作用域的變量對象,因此「創建」過程一定是在代碼執行前完成,也就是一定會被提升,那為了防止大家在聲明之前就使用,就在「初始化」上做文章了,沒有講「初始化」不提升,這樣就不能在聲明之前使用了。
參考:
阮一峰《ES6標准入門》