JavaScript 初學者教學:認識函式的「選擇性參數」(Optional Parameters)

Published July 13, 2025 by 徐培鈞
JavaScript

在學習 JavaScript 的過程中,你可能會發現一個很特別的現象:

當我們呼叫某個函式時,傳入的參數數量似乎不需要完全符合函式定義時所寫的數量,甚至可以多傳幾個參數,或是少傳,程式也不會報錯,依然能夠正常執行。

讓我們來看一個簡單的例子:

function greet(name) {
  console.log("Hello, " + name + "!");
}

greet("Alice");         // → Hello, Alice!
greet("Bob", "Hi");     // → Hello, Bob!
greet();                // → Hello, undefined!

在這個例子中,我們定義了一個 greet 函式,它只需要一個 name 參數:

  1. 當我們只傳入一個參數("Alice")時,會正常顯示 Hello, Alice!
  2. 當我們多傳一個參數("Hi"),JavaScript 會忽略第二個多出來的參數,仍然只拿 "Bob" 當作 name
  3. 更特別的是,如果我們什麼都不傳name 的值會變成 undefined,這樣程式仍然可以執行,只是輸出會是 Hello, undefined!

這到底是怎麼回事呢?這正是 JavaScript 函式的一個重要特性:「參數是選擇性的(optional)」,也就是說——

JavaScript 函式並不會嚴格檢查參數的數量,只會根據實際傳入的值來運作。

這種彈性設計雖然讓 JavaScript 程式變得更寬容、更方便,但也容易讓初學者出現誤解或執行上的錯誤。

如果不小心少傳參數,可能會出現 undefined 造成不預期的結果;如果多傳,也可能讓多餘的資訊被忽略掉而浪費資源。

因此,這篇文章會一步步帶你深入理解這種「選擇性參數」的運作方式,讓你在寫函式或呼叫函式時能夠更有信心、也更不容易出錯。

什麼是「選擇性參數」?

在很多程式語言中,當你呼叫一個函式時,必須準確地傳入所有需要的參數,否則會出錯或中斷執行。

但在 JavaScript 中,這件事情被設計得「很寬鬆」。也就是說:

JavaScript 的函式可以接收比你定義時「更多」或「更少」的參數,而且不會報錯。

這種特性讓開發者在使用函式時更有彈性,也因此產生了所謂的「選擇性參數」概念(Optional Parameters)。我們可以根據實際情況決定是否要傳入某些參數,而函式會依照你提供的參數,自動調整自己的行為。

範例一:參數傳太多了怎麼辦?

function square(x) {
  return x * x;
}

console.log(square(4, true, "hedgehog"));
// 輸出:16

說明:

上面這段程式碼中,我們定義了一個 square 函式,它只需要一個參數 x,並回傳 x * x 的結果。照理來說,我們應該只傳入一個數字,例如 square(4)

但實際上,我們傳入了三個參數:

square(4, true, "hedgehog");

神奇的是,JavaScript 並不會報錯,也不會警告你傳太多參數了。它只會乖乖地:

  • 把第一個參數 4 指派給變數 x
  • 完全忽略後面的 true"hedgehog"

也就是說,執行過程中 square(4, true, "hedgehog") 的效果,其實和 square(4) 一模一樣。因為函式裡只使用了 x,根本沒有定義或處理後面的參數。

這種「超量容忍」的特性,是 JavaScript 的一種預設行為。雖然很方便,但在複雜程式中如果不小心多傳了參數,卻被默默忽略,可能會讓錯誤變得更難發現。

如果參數傳太少又會怎樣?

那反過來,如果我們傳入的參數「比函式需要的還少」呢?答案是:

JavaScript 會自動幫你把「缺少的參數」補上 undefined

也就是說,函式裡的參數變數還是存在,只是它們的值是 undefined

傳少了參數,函式會怎麼反應?

function minus(a, b) {
  if (b === undefined) return -a;
  else return a - b;
}

console.log(minus(10));
// 輸出:-10

console.log(minus(10, 5));
// 輸出:5

說明:

這個 minus 函式的設計很特別,它有兩種行為:

  1. 如果只傳一個參數,也就是 b 沒有傳,會進入 if 判斷,把 b === undefined 判定為 true,結果就會回傳 -a
  2. 如果兩個參數都傳了,就會回傳 a - b,進行一般的減法。
minus(10);      // 只傳 a,b 是 undefined,所以回傳 -10
minus(10, 5);   // 傳 a 和 b,正常減法,結果是 5

這樣的技巧讓函式變得更有彈性,也讓你可以根據「實際傳入的參數數量」來決定執行邏輯。

這樣設計有什麼風險?

雖然選擇性參數聽起來很方便,但它也有潛在風險:

  • 錯誤可能被「靜靜地吞掉」:如果你不小心少傳了參數,JavaScript 不會告訴你,只會默默給它 undefined,這在某些情況下可能導致不明錯誤或奇怪的運算結果。
  • 維護時不易追錯:當你在大型專案中修改函式,或是別人呼叫你的函式時,若沒明確註明哪些參數是必要的,別人可能會誤用或忽略重要參數。

因此,雖然這是 JavaScript 的一種自由特性,但在使用時仍然建議加上:

  • 明確的預設值
  • 條件判斷是否有傳入參數
  • 或是使用 TypeScript(可選)來進一步嚴格檢查

預設值怎麼用?

在前面的章節我們提到,當函式的參數沒被傳入時,它的值就會變成 undefined

這雖然是 JavaScript 的正常行為,但有時候我們會希望:「如果使用者沒傳這個參數,就自動幫他填一個預設的值」,這樣程式比較不容易出錯。

為了解決這個問題,JavaScript 提供了預設參數值(default parameter)的語法,讓你可以在函式定義時直接寫好「備用值」。

範例一:點飲料,沒說糖度就給半糖

我們來看一個生活化的例子。

想像你寫了一個點飲料的函式,讓使用者輸入飲料名稱和糖度。

如果他沒說糖度,就預設給他「半糖」。

function orderDrink(drink, sugar = "半糖") {
  console.log(`您點了一杯 ${sugar}${drink}`);
}

orderDrink("珍奶");
// → 您點了一杯 半糖 的 珍奶

orderDrink("紅茶", "無糖");
// → 您點了一杯 無糖 的 紅茶

說明:

這裡的 sugar = "半糖" 就是設定預設值的寫法。

  • 第一個例子只傳了「飲料名稱」,沒講糖度,所以函式自動套用預設的「半糖」。
  • 第二個例子明確指定「無糖」,函式就會使用你提供的值。

這樣做的好處是,使用者可以只填他在意的選項,其他就讓系統幫忙補上合理的預設值。

傳入 undefined,也會使用預設值

orderDrink("綠茶", undefined);
// → 您點了一杯 半糖 的 綠茶

這裡雖然有傳入第二個參數,但值是 undefined,JavaScript 仍然會套用預設的「半糖」。

但要注意,如果你傳的是 null,那就不會使用預設值囉:

orderDrink("烏龍茶", null);
// → 您點了一杯 null 的 烏龍茶

這是因為 null 是一個「有效的值」,不是「沒傳東西」,所以函式會照單全收。

預設值讓程式更有彈性

使用預設值有幾個好處:

  • ✔️ 使用者可以少打一點參數,程式幫忙補上合理預設
  • ✔️ 不怕沒傳參數導致 undefined 出錯
  • ✔️ 程式碼更清楚也更容易維護

這種寫法在開發中非常常見,尤其當你的函式會被不同人、在不同情境下使用時,設定好預設值就是一種保險。

如何接收不定數量的參數?

有時候,在寫函式的時候,我們不會事先知道「使用者會傳幾個參數」給這個函式。

例如,你寫一個「點餐」的函式,有的人只點一樣餐點,有的人點了五樣、十樣都有可能。

這時候如果函式只能固定接兩個、三個參數,就會很不方便。

所以 JavaScript 提供了一個超實用的語法,叫做 ...(三個點),這種寫法叫做「剩餘參數(rest parameter)」。

它的功能是:

🔧 把你呼叫函式時「多個傳進來的參數」,全部打包收集起來,變成一個陣列。

陣列是什麼?

當我們需要儲存很多資料時,可以用很多變數這樣寫:

let item1 = "薯條";
let item2 = "雞塊";
let item3 = "可樂";

雖然這樣可以記錄三筆資料,但一旦資料變多,就會變得非常不好管理。

這時候,我們就可以用「陣列」來幫忙!

✅ 陣列是一種「可以一次放很多資料」的變數

你可以把陣列想成一排收納格,像這樣:

櫃子 = [ "薯條", "雞塊", "可樂" ]
格子編號:   0       1       2

在 JavaScript 中,你可以用這種方式建立一個陣列:

let snacks = ["薯條", "雞塊", "可樂"];

這個陣列 snacks 裡面放了三個字串,分別代表三樣東西。而每一樣東西都有一個「位置編號」,也就是我們說的 索引(index)

這個編號從 0 開始,不是從 1。

🧠 為什麼從 0 開始?

JavaScript(還有很多程式語言)在設計上,就是用「第 0 格」代表第一筆資料,這是電腦記憶體結構的習慣。

如果你之後學更多語言,也會發現這是一種共通規則。

🧪 如何從陣列中拿出資料?

如果你要拿出陣列裡的某一筆資料,可以用「陣列名稱 + 中括號」的寫法,像這樣:

console.log(snacks[0]); // → 薯條
console.log(snacks[1]); // → 雞塊
console.log(snacks[2]); // → 可樂

這表示:

  • snacks[0] 是第一筆資料
  • snacks[1] 是第二筆資料
  • snacks[2] 是第三筆資料

這樣我們就可以很方便地一次儲存、一次處理多筆資料,而不用寫一堆變數。

🧰 陣列還能做什麼?

除了存東西、拿東西,陣列還有很多很強大的功能,例如:

  • .length:看裡面有幾筆資料
  • .push():往陣列後面加東西
  • .pop():把最後一筆資料拿掉
  • for 迴圈:一筆一筆跑過整個陣列

...參數 到底怎麼把參數變成陣列?

當你在定義函式時,用 ...參數名 這個語法,JavaScript 就會自動幫你做一件事:

把「你傳進來的所有參數」,通通打包成一個陣列!

來看一個實際例子:

function orderFood(...items) {
  console.log(items);
}

orderFood("滷肉飯", "紅茶", "炸雞");

執行結果會是:

["滷肉飯", "紅茶", "炸雞"]

也就是說,雖然我們傳進去的是三個獨立的值:

"滷肉飯", "紅茶", "炸雞"

但在函式裡面,它們會自動被包起來,變成這樣:

let items = ["滷肉飯", "紅茶", "炸雞"];

這個 items 就是陣列,裡面裝的就是你傳進來的所有資料。

🧠 換句話說

當你這樣定義函式:

function something(...data) { }

那麼你呼叫:

something("A", "B", "C");

JavaScript 背後其實幫你做的事情是:

let data = ["A", "B", "C"];

你就可以用 data[0]data[1] 去讀取每一筆資料,也可以用 data.length 看總共有幾筆。

為什麼這很重要?

在寫函式的時候,最麻煩的一件事就是:「我不知道別人會傳幾個參數給我!」

傳太少 → 可能會出錯;
傳太多 → 傳進來卻沒人接,白白浪費;
而且有時候你還得寫很多 if 去檢查有沒有傳值。

但如果你使用 ...參數(剩餘參數語法),這些問題就會簡單很多。

因為它會把你傳進來的所有參數自動包成一個陣列,你就能像處理一份清單一樣,一次處理全部的資料

而且你不需要自己一個一個接,只要寫一個變數名,就能把參數通通接起來,這是它最厲害的地方。

🧪 範例:點餐時接收不定數量的菜名

現在我們來寫一個點餐系統的函式。

這個函式叫做 orderFood,會印出使用者點了哪些菜。

你不確定對方會點幾道菜,所以你用 ...dishes 這個語法,讓函式可以一次接收「任意數量」的菜名。

function orderFood(...dishes) {
  console.log("您點的餐有:");
  for (let i = 0; i < dishes.length; i++) {
    console.log("- " + dishes[i]);
  }
}

這個函式裡做了三件事:

  1. 宣告一個剩餘參數 ...dishes,把所有傳進來的菜名打包成陣列
  2. 印出提示文列裡字「您點的餐有:」
  3. for 回圈,一個一個印出陣的每一道菜

現在來呼叫這個函式看看:

orderFood("滷肉飯");
orderFood("牛肉麵", "水餃", "紅茶");

🖨️ 執行結果:

您點的餐有:
- 滷肉飯

您點的餐有:
- 牛肉麵
- 水餃
- 紅茶

🧠 怎麼理解這段程式?

我們來仔細看看這行:

function orderFood(...dishes)

這裡的 ...dishes 是最重要的關鍵:

說明三個點的語法,代表「剩餘參數」的開始
說明接收的變數名稱,可以自己取名(這裡用 dishes 代表「一道道菜」)

🧩 程式內部實際的變化會長這樣:

當你這樣呼叫:

orderFood("牛肉麵", "水餃", "紅茶");

JavaScript 背後會幫你把這三個參數打包成陣列:

let dishes = ["牛肉麵", "水餃", "紅茶"];

所以你可以像處理陣列一樣,一個一個拿出來印出來、加總、統計都可以。

🧪 再進一步觀察:為什麼 dishes.length 可以用?

因為 dishes 是一個陣列,而陣列都有 .length 屬性,表示它裡面總共有幾筆資料。

console.log(dishes.length); // → 3

而我們用這個 .length 來跑 for 迴圈:

for (let i = 0; i < dishes.length; i++) {
  console.log("- " + dishes[i]);
}
  • i 會從 0 開始
  • 每次跑一圈,就印出 dishes[i]
  • 一直跑到印完最後一筆為止

這就是一種最基本的「一個一個跑過陣列」的方式。

結語:掌握彈性,提高程式設計能力

「選擇性參數」是 JavaScript 函式設計的一個重要特色,掌握這個特性不僅能夠提高你撰寫函式的靈活度,也能夠讓你更容易讀懂別人所寫的程式碼。

優點:

  • 使用彈性高,可根據情境調整參數數量。
  • 可為函式參數設定預設值,提升程式碼的可讀性。

缺點(要注意的地方):

  • JavaScript 不會自動提醒你參數數量有問題,容易造成程式運作上的疏漏。
  • 使用時必須更加注意參數傳入的狀況,避免產生意外的錯誤。\

在未來學習更多進階技巧時,這些基礎觀念都會成為你最大的助力。

希望透過這篇文章,你能夠深入理解並靈活運用這些參數技巧!