本文 AI 產出,尚未審核

JavaScript 函式參數與預設值

簡介

在日常的前端開發中,函式是最常用的抽象單位。無論是處理使用者輸入、呼叫 API,或是執行動畫,幾乎都會寫大量的函式。
函式的參數決定了它可以接受哪種資訊,而預設值則讓函式在呼叫者忘記提供某些參數時,仍能安全且可預測地執行。掌握參數與預設值的寫法與原理,能顯著提升程式的可讀性、可維護性與防呆能力,是每位 JavaScript 開發者必備的基礎功。

本篇文章將從語法、執行機制、常見陷阱,到實務上的最佳實踐與應用情境,完整說明「函式參數與預設值」的核心概念,讓你在寫程式時不再因參數錯位或未傳值而卡關。

核心概念

1. 參數的基本寫法

在 JavaScript 中,函式的參數列在圓括號內,呼叫時依序傳入對應的值。若呼叫時少傳參數,未提供的參數會自動得到 undefined

function greet(name) {
  console.log(`Hello, ${name}!`);
}

greet('Alice');   // Hello, Alice!
greet();          // Hello, undefined!

註: undefined 代表「未定義」的值,若直接使用會產生 NaN、字串 "undefined" 等意外結果。


2. ES6 以前的預設值寫法

在 ES6 (ES2015) 之前,我們只能在函式內部手動檢查參數是否為 undefined,再自行賦值。

function add(a, b) {
  // 若 b 為 undefined,則預設為 0
  if (b === undefined) {
    b = 0;
  }
  return a + b;
}

console.log(add(5, 3)); // 8
console.log(add(5));    // 5

這種寫法雖然可行,但冗長且易忘,尤其當參數很多時更顯笨拙。


3. ES6+ 的預設參數語法

ES6 引入了預設參數(default parameters),直接在參數列表中指定預設值,語法簡潔且易讀。

function multiply(x, y = 1) {
  return x * y;
}

console.log(multiply(7, 2)); // 14
console.log(multiply(7));    // 7   (y 會自動使用預設值 1)
  • 預設值可以是任意表達式,甚至是函式呼叫的結果。
  • 預設值只有在傳入的參數為 undefined 時才會生效,傳入 null0、空字串等仍會保留原值。
function greet(name = '匿名使用者') {
  console.log(`Hi, ${name}`);
}
greet(null);   // Hi, null   (null 不是 undefined)
greet();       // Hi, 匿名使用者

4. 參數解構與預設值的結合

當函式接受 物件陣列 作為參數時,常會使用解構賦值(destructuring)配合預設值,讓呼叫者只需提供需要的屬性。

function createUser({ name = 'Guest', age = 18, isAdmin = false } = {}) {
  return { name, age, isAdmin };
}

// 只提供 name,其他使用預設值
console.log(createUser({ name: 'Bob' }));
// => { name: 'Bob', age: 18, isAdmin: false }

// 完全不提供參數,整個參數物件也使用預設值 {}
console.log(createUser());
// => { name: 'Guest', age: 18, isAdmin: false }

重點: 解構參數本身也需要一個預設值(上例的 = {}),否則在呼叫 createUser() 時會拋出 TypeError: Cannot destructure property ... of 'undefined'.


5. 參數的「剩餘」與「預設」結合(Rest Parameters)

有時需要接收不確定數量的參數,...rest 允許把多餘的參數收集成陣列。預設值仍可放在前面的普通參數上。

function sum(base = 0, ...numbers) {
  return numbers.reduce((acc, cur) => acc + cur, base);
}

console.log(sum(10, 1, 2, 3)); // 16  (10 + 1 + 2 + 3)
console.log(sum());           // 0   (base 使用預設值 0,numbers 為 [])

常見陷阱與最佳實踐

陷阱 說明 解決方式
預設值寫在左側 function f(a = 1, b) {} 會讓 b 永遠得到 undefined,因為 a 的預設值不會影響 b 的傳入位置。 將有預設值的參數放在最右側,或使用解構物件傳參。
預設值是可變物件 預設值若是陣列或物件,會在函式定義時 只建立一次,多次呼叫會共享同一個參考,導致意外的副作用。 使用 函式返回新物件 作為預設值:function f(opts = (() => ({}))()) {} 或在函式內部重新建立。
忘記給解構參數的預設空物件 function foo({a = 1} = {}) {} 必須在外層加 = {},否則 foo() 會拋錯。 總是給解構參數一個空物件預設值
預設值依賴於其他參數 預設值只能使用前面已定義的參數,不能參考後面的參數。 把相依的計算寫在函式體內,或調整參數順序。
使用 arguments 與預設值混用 arguments 會包含所有實際傳入的參數,不會自動補上預設值,可能造成不一致。 盡量使用 ...rest 取代 arguments,或手動補足預設值。

最佳實踐

  1. 把有預設值的參數放在最後,或改用物件解構,提升呼叫的彈性。
  2. 預設值使用不可變資料(如字串、數字、null),若需預設陣列/物件,請在函式內部建立新實例。
  3. 利用解構與預設值,讓 API 更具自說明性,例如 function fetchData({ url, method = 'GET' } = {})
  4. 保持參數數量一致:若函式需要多個選項,盡量統一使用單一物件參數,避免位置參數錯位。
  5. 配合 TypeScript/JSDoc,為預設參數加上型別註記,提升編輯器自動完成與錯誤檢查。

實際應用場景

1. API 呼叫封裝

在前端專案中,常會寫一個統一的 request 函式,讓每次呼叫 API 時只需提供必要資訊,其他參數使用預設值。

function request({
  url,
  method = 'GET',
  headers = { 'Content-Type': 'application/json' },
  body = null,
  timeout = 5000
} = {}) {
  const controller = new AbortController();
  const id = setTimeout(() => controller.abort(), timeout);

  return fetch(url, {
    method,
    headers,
    body: body ? JSON.stringify(body) : null,
    signal: controller.signal
  })
    .finally(() => clearTimeout(id));
}

// 呼叫時只寫 url,其他自動套用預設
request({ url: '/api/users' })
  .then(res => res.json())
  .then(data => console.log(data));

2. 表單驗證函式

表單欄位的驗證規則常會有「必填」與「長度」等選項,使用預設值可以讓開發者只關注需要變動的規則。

function validate(value, {
  required = false,
  minLength = 0,
  maxLength = Infinity,
  pattern = null
} = {}) {
  if (required && !value) return false;
  if (value.length < minLength) return false;
  if (value.length > maxLength) return false;
  if (pattern && !pattern.test(value)) return false;
  return true;
}

// 只需要驗證必填與最小長度
const ok = validate('abc', { required: true, minLength: 3 });
console.log(ok); // true

3. 動畫參數設定

在使用 requestAnimationFrame 或第三方動畫函式庫時,常會提供多個可選參數,如持續時間、緩動函式等。

function animate({
  duration = 300,
  easing = t => t,   // linear
  onUpdate = () => {}
} = {}) {
  const start = performance.now();

  function step(timestamp) {
    const elapsed = timestamp - start;
    const progress = Math.min(elapsed / duration, 1);
    const eased = easing(progress);
    onUpdate(eased);
    if (progress < 1) requestAnimationFrame(step);
  }
  requestAnimationFrame(step);
}

// 呼叫時只想改變緩動函式
animate({
  easing: t => t * t,
  onUpdate: v => console.log(`progress: ${v}`)
});

總結

函式參數與預設值是 JavaScript 中提升程式彈性與安全性的關鍵工具。透過 ES6+ 的預設參數語法解構賦值、以及 Rest Parameters,我們可以寫出簡潔、易讀且防呆的函式 API。
在實務開發中,務必注意「預設值的可變物件共享問題」與「解構參數的空物件預設」等常見陷阱,並遵循 將預設參數放在最右側使用不可變資料作預設值統一以物件傳遞多個選項 等最佳實踐。

掌握了這些概念後,你將能更自如地設計函式介面,減少錯誤、提升程式可維護性,為大型前端專案奠定穩固的基礎。祝你寫程式愉快,持續探索 JavaScript 的無限可能!