P27–29:Hoisting / TDZ(var vs let/const)互動範例

目標:讓學生看到 var hoistinglet/const TDZ的差異(以及 function hoisting)。

什麼是 Hoisting(提升)?

Hoisting 是 JavaScript 引擎在執行代碼前的一個特殊機制:

Step 1: var 的提升機制

步驟 1: 看下面的原始代碼:
console.log(myVar); // 輸出什麼?
var myVar = 10;
步驟 2: JavaScript 引擎在解析階段會將宣告提升到作用域頂部:
// 解析階段後的代碼
var myVar; // 初始化為 undefined
console.log(myVar); // undefined
myVar = 10;
步驟 3: 執行結果:
undefined
> undefined > (anonymous) @ VM1:1

Step 2: let/const 的 TDZ (Temporal Dead Zone)

步驟 1: 看下面的原始代碼:
console.log(myLet); // 輸出什麼?
let myLet = 20;
步驟 2: JavaScript 引擎在解析階段會提升宣告,但不初始化,進入 TDZ:
// 解析階段後的代碼
let myLet; // 未初始化,進入 TDZ
console.log(myLet); // ReferenceError!
myLet = 20;
步驟 3: 執行結果:
ReferenceError: Cannot access 'myLet' before initialization
> Uncaught ReferenceError: Cannot access 'myLet' before initialization > at <anonymous>:1:13

Step 3: 完整比較示例

步驟 1: 完整的比較代碼:
// var 的情況
console.log(varExample); // undefined
var varExample = 'Hello';

// let 的情況
console.log(letExample); // ReferenceError
let letExample = 'World';

// const 的情況
console.log(constExample); // ReferenceError
const constExample = '!';
步驟 2: JavaScript 引擎如何處理:
// 解析階段後的代碼
var varExample;    // 初始化為 undefined
let letExample;    // 未初始化,進入 TDZ
const constExample; // 未初始化,進入 TDZ

console.log(varExample); // undefined
varExample = 'Hello';

console.log(letExample); // ReferenceError!
letExample = 'World';

console.log(constExample); // ReferenceError!
constExample = '!';
步驟 3: 實際控制台輸出:
> undefined > Uncaught ReferenceError: Cannot access 'letExample' before initialization > Uncaught ReferenceError: Cannot access 'constExample' before initialization

Function Hoisting(函數宣告可以先呼叫)

函數宣告(Function Declaration)會被完全提升,包含函數的整個定義:

// 你寫的原始程式碼:
sayHello(); // 即使在宣告之前也可以呼叫!

function sayHello() {
  console.log('Hello World!');
}
// JavaScript 引擎實際上會這樣處理:
function sayHello() {
  console.log('Hello World!');
}

sayHello(); // 函數已完全定義,所以可以正常執行
> Hello World!

重點: 這意味著你可以先呼叫函數,然後再定義它。這是函數宣告(Function Declaration)的特有行為。

Function Expression(函數表達式)與 var/let 的差異

函數表達式(Function Expression)的行為與函數宣告(Function Declaration)完全不同:

var 函數表達式:

// var 函數表達式
myFunc(); // TypeError: myFunc is not a function

var myFunc = function() {
  console.log('This is myFunc');
};
// JavaScript 引擎實際上會這樣處理:
var myFunc; // 提升為 undefined
myFunc(); // myFunc 是 undefined,不是函數,所以 TypeError
myFunc = function() {
  console.log('This is myFunc');
};
> TypeError: myFunc is not a function

let 函數表達式:

// let 函數表達式
myLetFunc(); // ReferenceError: Cannot access before initialization

let myLetFunc = function() {
  console.log('This is myLetFunc');
};
// JavaScript 引擎實際上會這樣處理:
let myLetFunc; // 提升但進入 TDZ
myLetFunc(); // 在 TDZ 中使用,所以 ReferenceError
myLetFunc = function() {
  console.log('This is myLetFunc');
};
> ReferenceError: Cannot access 'myLetFunc' before initialization

總結:

操作

建議教學節驟:先讓學生「猜結果」→ 按按鈕驗證 → 你再總結:
var:宣告會被提升(hoist),初值是 undefined
let/const:有 TDZ(Temporal Dead Zone),宣告前使用會 ReferenceError
function 宣告:整個函式會被提升,所以可先呼叫

輸出(Console-like)


          

也可打開 DevTools Console 對照(F12)。