在 JavaScript 中,函式(Function)不只是讓我們把程式碼包裝起來以便重複使用的工具,它更是一種「值」,就像數字、字串或物件一樣,可以被儲存到變數中、當作參數傳遞,甚至重新賦值。
這對初學者來說是一個關鍵觀念,因為理解「函式值」能幫助你寫出更靈活且強大的程式。
本文將以簡單易懂的方式,帶你一步步了解什麼是函式值、如何定義、指派與重新賦值,並結合實際範例幫助你掌握這項技能。
什麼是函式值?
函式不只是功能,它也是一種值
在 JavaScript 中,函式(Function)通常被認為是用來執行某段特定邏輯的工具,例如輸出一段文字、計算數學結果等等。
但你可能還不知道,其實函式本身也是一種值,就像數字、字串、布林值一樣,可以被指派給變數、存在陣列中、當作引數傳遞給其他函式,甚至作為回傳值。
這樣的函式就被稱為 「函式值」(Function Value),它的特點是:
✅ 可以儲存到變數
✅ 可以傳來傳去
✅ 可以被覆蓋、取代
函式值就像其他變數型別一樣使用
在學 JavaScript 的過程中,你可能已經很熟悉這樣的變數宣告方式:
let name = "Alice"; // 一個字串值
let age = 20; // 一個數字值
let isStudent = true; // 一個布林值函式值的用法其實也一樣,只是儲存的不是字串或數字,而是一段可以執行的程式碼邏輯,舉例如下:
let greet = function() {
console.log("Hello!");
};在這裡,greet 是一個變數,它儲存的是一個「匿名函式」——也就是沒有名稱的函式。你可以像呼叫其他函式那樣使用它:
greet(); // 輸出:Hello!也就是說,這個變數 greet 就代表這段函式本身,它可以被執行、傳遞、覆蓋,就像其他型別的資料值一樣操作自如。
為什麼要這樣設計?
在 JavaScript 裡,函式不只能執行事情,也能像數字、字串一樣,被存在變數裡、當作參數傳來傳去,或根據需求換掉。
這讓程式在處理使用者互動、功能組合或條件邏輯時更靈活。
舉個比較常見的例子:
你有一個彈跳視窗(modal),裡面有個「確定」按鈕。這個按鈕的功能可能會根據上下文不同而變化:
- 在刪除項目時,點「確定」要刪除資料
- 在登出時,點「確定」要執行登出動作
- 在送出表單時,點「確定」要傳送資料
這三個場景用的是同一個彈跳視窗元件,但「確定」按下去要做什麼不一樣。
這時候你就可以用「函式值」的概念,把每種情境下要做的動作寫成一個函式,然後傳進去給這個視窗元件使用。彈跳視窗不用知道細節,只要在按下確定時「執行你傳給它的函式」就好。
這就是為什麼 JavaScript 讓你可以把函式當作「值」:
👉 因為這樣你可以把功能拆開、抽出來、組合起來,不同場景用不同邏輯,程式也不會亂成一團。
範例:函式值的三種常見用法
在 JavaScript 裡,函式不只能執行動作,也可以像數字、字串一樣「綁定」到變數上,變成一個可以操作的值。
這代表:
- 你可以用變數名稱來執行這段函式
- 可以把這段功能傳給別的函式使用
- 甚至可以根據情況,把它重新綁定到另一段不同的行為上
我們來看三個非常實用的範例:
把函式綁定到變數上,之後就用變數來執行
let sayHi = function() {
console.log("Hi!");
};
sayHi(); // 輸出:Hi!這邊的重點是:
我們定義了一個匿名函式,然後把它綁定到變數 sayHi 上。
從這一刻開始,sayHi() 就代表這段函式,可以直接呼叫來執行。
這種方式的好處是:
- 把一段功能封裝起來,有名字可以呼叫
- 方便管理和重複使用
- 之後還能把這個變數傳給別人用(下一段會講)
這樣的「函式綁定」幾乎是寫 JavaScript 的基本套路,像你寫工具函式、事件處理器、或模組化功能時,幾乎都會用這種方式。
把函式當作參數,傳給別的函式去執行(callback)
JavaScript 的強大就在於你可以把函式當成一個資料值傳來傳去。
當你把它傳進另一個函式,讓對方「在適當時候再執行」,這就是所謂的 callback(回呼函式)。
function greetUser(callback) {
console.log("準備打招呼...");
callback(); // 執行傳進來的函式
}
let sayHello = function() {
console.log("Hello!");
};
greetUser(sayHello); // 輸出:準備打招呼... 然後 Hello!這段的邏輯很清楚:
- 我們先定義好一段動作
sayHello,並綁定到一個變數上 - 然後把這個變數當參數傳進
greetUser greetUser收到之後,就在裡面把它執行掉
你可以想像成:「我不管你要做什麼,我只負責叫你執行你自己綁的那段邏輯。」
這種寫法在很多情境都會用到,例如:
- 使用者點按鈕時要執行某個動作(onClick)
- 資料載入完成後要更新畫面
- 計時器結束後要做某件事
它讓程式變得更彈性、模組化,邏輯也不會全部寫死在同一個地方。
根據情況重新綁定不同的函式(動態改變行為)
因為函式可以綁定在變數上,所以你當然也可以根據條件,把它換成另一段不一樣的邏輯,這樣就可以控制程式在不同情況下的行為。
let launchMissiles = function() {
missileSystem.launch("now");
};
if (safeMode) {
launchMissiles = function() {
// 安全模式不做任何事
};
}
launchMissiles();這段的重點在於:
launchMissiles原本綁的是一段真正會發射飛彈的函式- 但如果現在進入「安全模式」,你就重新綁定一段空的函式上去
- 這樣之後不管誰呼叫
launchMissiles(),都只會執行「什麼都不做」的版本
這種做法的實際用途很多,像是:
- 測試階段先避免真正執行敏感操作
- 沒有權限的使用者呼叫某功能時直接略過
- 開關控制(某功能未啟用時暫時無效)
這比在每個地方都加 if 判斷來得簡潔又清楚。
小結:函式當成「值」綁在變數上,讓程式更有彈性
| 用法 | 概念 | 能幫你做到什麼 |
|---|---|---|
| 綁定函式到變數 | 給一段功能一個名字 | 方便呼叫、重複使用 |
| 傳給其他函式使用 | 讓別人決定何時執行 | 提高彈性、拆分邏輯 |
| 重新綁定不同內容 | 根據狀況切換行為 | 開關控制、功能替換 |
這就是為什麼 JavaScript 要讓函式能「綁定」到變數、能當成值操作──因為這會讓你的程式寫起來更乾淨、邏輯更清楚,也更好維護。
如果你接下來想進一步學習事件處理、非同步流程、React 的 props 或 Vue 的 method 綁定,這些觀念都會是你必備的基礎。
函式值 vs 函式名稱:容易混淆的地方
很多人學到這邊會搞混:這兩種寫法有什麼差?
在 JavaScript 中,函式有兩種最常見的寫法:
A. 直接用名稱宣告一個函式(Function Declaration)
function foo() {
console.log("我是一個函式名稱");
}這種寫法叫做「函式宣告」,寫法簡單清楚,一看就知道 foo 是一個函式。
B. 把函式當成值,賦值給變數(Function Expression)
let foo = function() {
console.log("我是一個被賦值給變數的函式");
};這種叫做「函式表達式」,看起來也沒什麼問題,但實際上有幾個重要的差別。
關鍵差異 1:foo 是「函式名稱」還是「變數名稱」?
這是最容易混淆的點。
在寫 function foo() {} 的時候,foo 是函式本身的名字,它就綁定在那個名字上,這段程式會直接把 foo 定義成一個可以呼叫的函式。
而 let foo = function() {} 是「建立一個匿名函式」,然後把它賦值給變數 foo。
這時候 foo 是一個變數名稱,不是函式本身的名稱。
所以,foo() 在兩種情況下雖然都能執行,但實際上他們的「身分」不同 ——
一個是直接宣告的函式名稱,一個是變數裡面裝著的函式值。
關鍵差異 2:能不能重新指定?
這個差異很明顯也很實用:
// 函式宣告 - 不能重新指定
function bar() {
console.log("原本的");
}
// ❌ 下面這樣會出錯(或無效)
bar = function() {
console.log("想換掉");
};// 函式值 - 可以重新指定
let bar = function() {
console.log("原本的");
};
bar = function() {
console.log("後來改的");
};如果你希望函式之後可能會根據情況換掉,那就要用函式值的方式。
關鍵差異 3:Hoisting(提升)行為不同
這一點是很多初學者卡住的地方。
為什麼有時候可以先呼叫函式再定義,有時候卻會報錯?
這和 JavaScript 的執行順序有關。
👣 JavaScript 的執行流程簡單來說是這樣:
- 程式碼會從上往下讀取、執行
- 在正式執行每一行程式碼之前,JavaScript 會先做一次「前置整理」
- 在這個整理階段,它會把某些東西「提前放好位置」,這就叫做 Hoisting(提升)
📌 哪些東西會被提前處理?
其中一個會被提前的東西就是「函式宣告」。
來看這段程式碼:
greet(); // ✅ 可以執行,輸出 Hello
function greet() {
console.log("Hello");
}你可能會覺得奇怪:greet() 明明寫在函式定義上面,為什麼不會出錯?
這就是因為 JavaScript 一開始就把這個 function greet() 宣告「先處理好」,雖然你寫在下面,它在背景裡已經準備好了。
❌ 但函式值就不一樣了
來看下面這個寫法:
greet(); // ❌ 錯誤:greet 還沒準備好
let greet = function() {
console.log("Hello");
};這裡的 greet 是一個變數,你只是把一個匿名函式「賦值」給它。
JavaScript 雖然知道你有宣告 let greet,但它不會幫你先把函式內容準備好。
它只會等到執行到那一行時,才真正把函式綁定進去。
所以當你一開始就寫 greet(),這時候變數雖然有宣告過,但裡面還是「空的」,還沒綁上函式,自然就報錯了。
更完整的比喻:主動報到 vs 等你叫才出現
要理解函式宣告和函式值的差別,你可以想像在辦一場活動,而你請了一群工作人員來協助。
這些人就是你定義的函式,等著上場執行任務。
🧍♂️ 函式宣告:提早報到、隨叫隨到的員工
function greet() {
console.log("Hello!");
}這種寫法就像你請了一位員工小李來幫忙,他非常積極 ——
活動開始前他就先來報到,站在指定位置,隨時待命。
所以你不管在程式哪裡呼叫他(即使是在他「出現之前」),他都會回應,因為他一開始就準備好了。
greet(); // ✅ 可執行,因為小李已經在現場這就是為什麼你可以先呼叫 greet(),再寫 function greet(),因為 JavaScript 執行前就已經把小李安排好了。這就是所謂的 Hoisting(提升)。
🧍 函式值:等你排班才會上工的臨時工
let greet = function() {
console.log("Hello!");
};這種寫法就像你跟一位臨時工小王說:「等我安排到你那行程式的時候再來上班。」
所以一開始活動開始時,小王還沒到場,只是「名單上有寫他要來」。
你必須執行到那一行程式碼時,JavaScript 才會真的安排他上場。
所以如果你太早呼叫:
greet(); // ❌ 會出錯:小王還沒上班!這時候程式會報錯:「找不到 greet」,因為那位員工還沒來現場。你寫的是「等下才要執行這行」,而不是預先安排好。
小總結
| 類型 | 行為說明 | 比喻 |
|---|---|---|
| 函式宣告 | 會被提早處理,整份程式一開始就能呼叫 | 小李:一開始就報到、站好位置、隨叫隨到 |
| 函式值(變數) | 要等執行到那一行,才把函式指派進去,才能用 | 小王:雖然名單上有寫,但要等你叫到才會來 |
這就是為什麼 函式宣告可以先呼叫、函式值不行。
如果你在寫程式時會有這種需求:
- 要在整份程式一開始就能呼叫的工具 → 用函式宣告
- 要根據不同情況決定內容(可能會換掉的) → 用函式值
記住這個「小李 vs 小王」的比喻,在你遇到錯誤訊息或混亂的時候,可以幫你快速搞懂發生了什麼事。
小提醒:哪一種該用在哪裡?
- 如果你只是單純定義一段功能,會在多個地方使用 → 用函式宣告
- 如果你要把函式當成資料傳來傳去、後續會換掉、或依情境決定內容 → 用函式值
- 如果你在寫 callback(回呼)、事件綁定、非同步邏輯 → 幾乎一定會用函式值
結語:學會函式值,打開 JavaScript 世界的大門
函式值是 JavaScript 中一個非常強大的特性,它讓函式可以像資料一樣被靈活使用。
理解它不僅能讓你寫出更具彈性的程式,也為你日後學習如 callback、事件監聽、Promise、async/await 等進階功能打下穩固基礎。
記住,函式不只是「做某件事」的代碼段,它們本身也是資料值,能夠被傳遞、賦值與重新定義。
從今天起,試著用這個觀點來看待你的函式,會有完全不同的收穫!