終於慢慢撿回 JS 的手感了(到底是要拖多久),繼前一篇瞭解基本變數和JS 難搞的地方,我們來看:函式宣告(Function Declaration)、函式表達式(Function Expression,包括匿名函式)、回呼函式(Callback Function),再到創作階段(Creation Phase)和執行階段(Execution Phase),一步步解釋,還會配合範例講解。
1. 函式宣告 (Function Declaration)
函式宣告是用 function 關鍵字直接定義一個有名字的函式。它的特點是可以在程式碼中的任何地方呼叫,因為 JS 引擎會在執行前把函式宣告「提升」(Hoisting)到作用域頂端。即使先 Call 函數再定義也沒問題。
公式:定義function,() 裡面是輸入的值,不寫任何東西也可以。function functionName(parameters) {
// 函式內容
return something;
}
實際操作:特別注意反引號跟單引號,反引號Windows 鍵盤:
通常在 Esc 下方、1 左邊 的那個鍵,跟 ~
是同一顆。
function sayHello(name) {
return `Hello, ${name}!`; // 注意!是反引號不是''單引號喔
}
console.log(sayHello("Bicky")); // 輸出: Hello, Bicky!
關鍵點:
- Hoisting:即使你先呼叫函式再定義它,程式也不會報錯,因為 JS 會把函式宣告「拉到」作用域頂端。
- 範例(證明 Hoisting):先叫出功能,再定義。
console.log(sayHi("Charlie")); // 輸出: Hi, Charlie!
function sayHi(name) {
return `Hi, ${name}!`;
}
2. 函式表達式 (Function Expression)
函式表達式是把函式賦值給一個變數。函式可以是有名字的,也可以是匿名函式(Anonymous Function)。它的特點是不會被 Hoisting,所以必須先定義才能呼叫。
公式:
const functionName = function [名字](parameters) {
// 函式內容
return something;
};
範例(匿名函式):
const greet = function(name) { // 他沒有取名字,是用變數
return `Greetings, ${name}!`;
};
console.log(greet("Bicky")); // 輸出: Greetings, Bicky!
範例(具名函式表達式)(常用在遞回:
3! = 3 × 2 × 1 = 6
5! = 5 × 4 × 3 × 2 × 1 = 120
const factorial = function cal(n) {
if (n <= 1) return 1;
return n * cal(n-1); // 一開始我寫n*n+1 忘記四則運算
};
console.log(factorial(5)); // 120
console.log(factorial(0)); // 1(0 的階乘是 1)
這是我練習的,來看看問題出在哪裡?
const factorial = function cal(n) { if (n <= 1) return 1; return cal(n*n+1); };
return cal(n * n + 1);
這一行是「把 n
自己平方再加一」,不是階乘啦!
階乘的定義是:
👉 n * (n - 1) * (n - 2) * ... * 1
建議在實作前先在紙上模擬
關鍵點:
- 匿名函式:函式表達式中的函式可以沒有名字(像第一個範例的 function(name)),這就是匿名函式。
- 無 Hoisting:因為是賦值給變數,只有變數宣告會被 Hoisting,函式本身不會。
- 範例(證明無 Hoisting):
console.log(sayBye("Eve")); // 錯誤: sayBye is not a function
const sayBye = function(name) {
return `Bye, ${name}!`;
};
3. 回呼函式 (Callback Function)
回呼函式是作為參數傳遞給另一個函式的函式,通常用於非同步操作(像是計時器、事件處理或 API 請求)。它讓程式可以在某個任務完成後「回頭呼叫」執行你的函式。白話一點的說是時機到了才會召喚他出來。
參考連結:MDN Web
真實場景(setTimeout):
function delayedGreeting(name) {
console.log(`Hello, ${name}!`);
}
setTimeout(function() {
delayedGreeting("Grace");
}, 5000); // 5秒後輸出: Hello, Grace!
關鍵點:
- 回呼函式通常是匿名函式(像 setTimeout 範例),但也可以是有名字的函式(像 greetCallback)。
- 它們是 JS 非同步 coding 的基礎,像是事件監聽器 (addEventListener) 或 Promise 處理。
稍微認識這些函式後,我們進到正式的作階段(Creation Phase)和執行階段(Execution Phase)。
1. 創作階段(Creation Phase)
像是 JS 幫你「排座位、先貼名牌」的準備期。
它會做三件事:
- 建立
global object
(像是window
或global
)讓後面的程式都可以用。 - 建立
this
(在 global 是指 global object) - 先掃過所有變數和函式宣告
所以:
- 函式會整個搬到上面(可以先呼叫)
var
的變數會「被提升」但值是undefined
let
/const
會進入「暫時性死區」不可以偷看(還沒貼名牌)
2. 執行階段(Execution Phase)
開始照順序跑你的程式碼、做事情,這時候才會給變數真正的值,跑邏輯、算式、執行函式。
運作的方式是
console.log(x); // undefined 還沒讀到x是誰
var x = 10;
sayHi(); // ✅
function sayHi() {
console.log("嗨!");
}
- 發現
var x
,貼名牌但值是undefined
- 發現
function sayHi()
,整個搬上來了
執行階段:
console.log(x)
👉 印出undefined
- 然後
x = 10
- 呼叫
sayHi()
👉 印出「嗨!」
最後小試身手 (請AI出題釐清觀念)
- 哪些函式先被 JS 貼名牌(創作階段)?
- 哪些會在程式跑的時候被呼叫(執行階段)?
- 函式裡的函式是怎麼被呼叫的(callback)?
function first(cb) {
console.log("1. 我是 first");
cb();
}
function second() {
console.log("2. 我是 second");
}
console.log("0. 開始");
first(second);
console.log("3. 全部結束");
我有點猜錯,我對於first(second);的運作還不熟悉,一開始沒有理解cd()是怎麼傳遞。來看一下解答:
- 創作階段:
- JS 掃過 function first(cb),貼名牌 first
- JS 掃過 function second(),貼名牌 second
- 執行階段:
- 從 console.log("0. 開始") 開始跑
- 呼叫 first(second):把 second 當參數傳給 cb
- first 裡印出 "1. 我是 first",然後 cb() 被呼叫,其實就是 second()
- 所以印出 "2. 我是 second"
- 然後跑到 console.log("3. 全部結束")