JavaScript 函式值教學:從定義、使用到實戰應用

Published July 12, 2025 by 徐培鈞
JavaScript

在 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!

這段的邏輯很清楚:

  1. 我們先定義好一段動作 sayHello,並綁定到一個變數上
  2. 然後把這個變數當參數傳進 greetUser
  3. 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 的執行流程簡單來說是這樣:

  1. 程式碼會從上往下讀取、執行
  2. 在正式執行每一行程式碼之前,JavaScript 會先做一次「前置整理」
  3. 在這個整理階段,它會把某些東西「提前放好位置」,這就叫做 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 等進階功能打下穩固基礎。

記住,函式不只是「做某件事」的代碼段,它們本身也是資料值,能夠被傳遞、賦值與重新定義。

從今天起,試著用這個觀點來看待你的函式,會有完全不同的收穫!