好的各位,我們今天要接續昨天的題目,來討論一個我在面試中遇到的 JavaScript 考題。
先讓我們重新複習一下題目:
請問下列程式碼執行結果為何?
try {
throw new Error();
}catch(x){
var x = 'X'
var y = 'Y'
const z = 'Z'
console.log(`${x} is in the catch`)
console.log(`${y} is in the catch`)
console.log(`${z} is in the catch`)
}
console.log(`${x} is outside the catch`)
console.log(`${y} is outside the catch`)
console.log(`${z} is outside the catch`)
在第一天,我們了解由於 try...catch 機制的關係,六行程式碼依序執行的順序會是:
console.log(${x} is in the catch
)
console.log(${y} is in the catch
)
console.log(${z} is in the catch
)
console.log(${x} is outside the catch
)
console.log(${y} is outside the catch
)
console.log(${z} is outside the catch
)
那麼在第二天,我們即將討論 JavaScript 變數提升的特性在這個題目中造成的影響。
開始!
在討論題目本身之前,讓我們先複習一下 var, let, const 三種變數的特性(這本身也是一個很常見的考題唷!)
作用域:var 為函數作用域、 let 跟 const 為區塊作用域。
① var 變數可重新聲明、重新賦值;
② let 變數不可重新聲明、可重新賦值;
③ const 不能重新聲明也不可以重新賦值。const 最嚴格,只有聲明為物件時才能改變物件內部的值。
三種變數都會被提升,但是提升之後的狀態不太一樣。var 在提升後,會被初始化為 undefined,而 let 跟 const 則會進入 「暫時性死區」(Temporary Dead Zone,簡稱 TDZ)
(暫時性死區?殭屍復活?)
如果常常跟我一樣去複習面試題的話,第 1、第 2 點應該都算熟悉,但針對第 3 點的「暫時性死區」可能也有點陌生。那是一個 介於「提升」與「賦值」之間的奇異時空!所以當 var 被提升後,我們還是可以存取到(顯示值為 undefined),但在 let 跟 const 進入「暫時性死區」後,我們試圖存取就會拋出錯誤,在控制台看到 ReferenceError: Cannot access ... before initialization
,此時程式碼暫停,不再往下執行。
回到題目本身,綜合以上三點的概念,我們透過註解來說明:
var x; // var 變數為函數作用域,被提升到外部,此時若存取 x 變數將獲得 undefined
var y; // var 變數為函數作用域,被提升到外部,此時若存取 y 變數將獲得 undefined
try {
throw new Error();
}catch(x){
const z; // const 變數為區塊作用域,於是提升到 catch 區塊的頂部,並進入 TDZ
x = 'X' // var x 被重新賦值為 'X'
y = 'Y' // var y 被重新賦值為 'Y'
z = 'Z' // 此處 z 變數才賦值為 'Z'
console.log(`${x} is in the catch`) // 存取到變數 x 的 'X'值
console.log(`${y} is in the catch`) // 存取到變數 y 的 'Y'值
console.log(`${z} is in the catch`) // 存取到變數 z 的 'Z'值
}
console.log(`${x} is outside the catch`) // 這裡會是什麼呢?明天繼續討論!
console.log(`${y} is outside the catch`) // 變數 y 是函數作用域,'Y'值仍可以被存取
console.log(`${z} is outside the catch`) // 變數 z 是區塊作用域,離開區塊後就無法存取,顯示 ReferenceError 拋出錯誤
綜合結論,以上的執行會在控制台看到的結果依序是:
X is in the catch
Y is in the catch
Z is in the catch
// 這裡會是什麼呢?明天繼續討論!
Y is outside the catch
Uncaught ReferenceError: z is not defined
好的,以上就是今天討論的變數提升概念,現在我們已經知道這 6 行代碼其中 5 行會印出的答案了~(灑花)
明天我們將討論 catch 的陷阱,也就是 console.log(${x} is outside the catch
) 這一行的答案。
我們明天見!
參考資料:
Huli:我知道你懂 hoisting,可是你了解到多深?
Eva:2021鐵人賽[Day 18] JS - 變數提升Hoisting