初學者指南:深入了解 JavaScript 的引數(Arguments)與參數(Parameters)

更新日期: 2024 年 10 月 23 日

在學習 JavaScript(或任何編程語言)時,理解引數(Arguments)和參數(Parameters)的概念是至關重要的。

這兩個術語經常被混淆,但它們在函式的定義和調用中扮演著不同的角色。

本文將為新手詳細介紹引數與參數的概念、差異,以及如何在 JavaScript 中正確地使用它們。


什麼是參數(Parameters)?

定義

參數是在定義函式時,放在括號 () 中的變數名稱,用於接收函式調用時傳入的值。

參數是函式的局部變數,僅在函式內部可見。

範例:

function greet(name) {
  console.log("Hello, " + name + "!");
}
  • name 就是這個函式的參數。

特點

  • 佔位符:參數是函式的佔位符,用於接收傳入的引數。
  • 作用域:參數僅在函式內部有效,屬於函式的局部作用域。
  • 可設定預設值:在 ES6 之後,參數可以有預設值。

範例(參數預設值):

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

greet(); // 輸出:Hello, Guest!

什麼是引數(Arguments)?

定義

引數是當函式被調用時,傳遞給函式的實際值。

這些值會被賦予給函式的參數。

範例:

greet("Alice");
  • "Alice" 就是這次函式調用傳遞的引數。

特點

  • 實際值:引數是函式調用時傳入的實際數據。
  • 數量可變:引數的數量可以與參數數量不同(多於或少於)。
  • 類陣列物件:在函式內部,可以使用 arguments 物件來訪問所有傳入的引數。

範例:

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

greet("Alice"); // 輸出:Hello, Alice!

arguments 物件

定義

arguments 是函式內部的內建物件,類似於陣列,包含了函式調用時所有傳入的引數。

特點

  • 類陣列物件:有 length 屬性,可以透過索引訪問元素,但不具備陣列的方法。
  • 僅在普通函式中可用:在箭頭函式中沒有 arguments 物件。

範例(使用 arguments):

function showArguments() {
  console.log(arguments);
}

showArguments(1, 2, 3); // 輸出:[1, 2, 3]

arguments 轉換為真正的陣列

可以使用 Array.from() 或展開運算符 ...arguments 轉換為真正的陣列。

範例:

function sum() {
  let args = Array.from(arguments);
  return args.reduce((total, current) => total + current, 0);
}

console.log(sum(1, 2, 3)); // 輸出:6

剩餘參數(Rest Parameters)

定義

剩餘參數使用 ... 語法,允許我們將不定數量的引數收集到一個陣列中。

範例:

function sum(...numbers) {
  return numbers.reduce((total, current) => total + current, 0);
}

console.log(sum(1, 2, 3, 4)); // 輸出:10

arguments 的差異

  • 真正的陣列:剩餘參數是陣列,可以直接使用陣列的方法。
  • 箭頭函式中可用:剩餘參數可以在任何函式中使用,包括箭頭函式。

範例(箭頭函式與剩餘參數):

const multiply = (...numbers) => {
  return numbers.reduce((total, current) => total * current, 1);
};

console.log(multiply(2, 3, 4)); // 輸出:24

參數與引數的關係

對應關係

  • 參數:定義函式時的變數名,作為接收引數的容器。
  • 引數:調用函式時傳入的值,賦予給函式的參數。

範例:

function multiply(a, b) { // a、b 是參數
  return a * b;
}

let result = multiply(5, 3); // 5、3 是引數
console.log(result); // 輸出:15

數量不一致時的行為

  • 引數少於參數:未被賦值的參數將是 undefined
  • 引數多於參數:多出的引數可以透過 arguments 物件或剩餘參數(Rest Parameters)來訪問。

範例(引數少於參數):

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

greet("Bob"); // 輸出:undefined, Bob!

範例(引數多於參數):

function showNames(firstName, lastName) {
  console.log("First Name: " + firstName);
  console.log("Last Name: " + lastName);
  console.log("Arguments Length: " + arguments.length);
}

showNames("John", "Doe", "Extra"); 
// 輸出:
// First Name: John
// Last Name: Doe
// Arguments Length: 3

參數的預設值

定義預設值

在 ES6 中,可以為函式的參數設定預設值,當引數未傳入或為 undefined 時,參數將使用預設值。

範例:

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

greet(); // 輸出:Hello, Guest!
greet("Alice"); // 輸出:Hello, Alice!
greet("Bob", "Hi"); // 輸出:Hi, Bob!

實際應用:arguments 參數自動調用

function sum() {
  let total = 0;
  for (let i = 0; i < arguments.length; i++) {
    total += arguments[i];
  }
  return total;
}

console.log(sum(1, 2, 3, 4)); // 輸出:10

關鍵概念

  • 函式的引數與參數
    • 參數:函式定義時的小括號內的變數名。在這個函式中,沒有定義參數,因此小括號內是空的。
    • 引數:函式調用時傳入的實際值。在這裡,我們傳入了 1、2、3、4。
  • arguments 物件
    • 是函式內部自動生成的,包含了所有傳入的引數。
    • 類似於陣列,可以透過索引訪問每個引數,但不具備完整的陣列方法。
  • 遍歷引數
    • 使用 for 迴圈,透過 arguments.length 獲取引數的數量,遍歷所有的引數。

執行流程詳解

  1. 函式調用
    • 調用 sum(1, 2, 3, 4);,傳入引數 1、2、3、4。
  2. 進入函式
    • arguments 物件包含了 [1, 2, 3, 4]
    • arguments.length 為 4。
  3. 初始化 total
    • total 被設置為 0。
  4. 開始迴圈
    • 第一次迭代 (i = 0)
      • arguments[0] 為 1。
      • total = total + arguments[0],即 total = 0 + 1 = 1
    • 第二次迭代 (i = 1)
      • arguments[1] 為 2。
      • total = total + arguments[1],即 total = 1 + 2 = 3
    • 第三次迭代 (i = 2)
      • arguments[2] 為 3。
      • total = total + arguments[2],即 total = 3 + 3 = 6
    • 第四次迭代 (i = 3)
      • arguments[3] 為 4。
      • total = total + arguments[3],即 total = 6 + 4 = 10
  5. 結束迴圈
    • 因為 i = 4,不小於 arguments.length,迴圈結束。
  6. 返回結果
    • 函式返回 total,即 10。
  7. 輸出結果
    • console.log 輸出 10。

為什麼使用 arguments 而不使用參數?

  • 靈活性:因為我們不知道會傳入多少個引數,所以沒有在函式定義中指定參數。
  • 適用於不定數量的引數arguments 物件允許我們處理任意數量的引數,非常適合用於需要累加或處理不定數量數據的情況。

改進:使用剩餘參數(Rest Parameters)

在 ES6 中,我們可以使用剩餘參數來實現相同的功能,更加簡潔。

改寫程式碼:

function sum(...numbers) {
  let total = 0;
  for (let i = 0; i < numbers.length; i++) {
    total += numbers[i];
  }
  return total;
}

console.log(sum(1, 2, 3, 4)); // 輸出:10

或者使用陣列方法:

function sum(...numbers) {
  return numbers.reduce((total, current) => total + current, 0);
}

console.log(sum(1, 2, 3, 4)); // 輸出:10
  • ...numbers:使用剩餘參數,將所有傳入的引數收集到一個陣列中,命名為 numbers
  • 好處
    • 代碼更清晰:不再需要使用 arguments 物件。
    • 真正的陣列numbers 是一個陣列,可以直接使用陣列的方法,如 reduce

實際案例二:預設值與解構賦值結合

可以結合解構賦值,為參數設定預設值。

範例:

function createUser({ name = "Guest", age = 18 } = {}) {
  console.log("Name: " + name);
  console.log("Age: " + age);
}

createUser(); 
// 輸出:
// Name: Guest
// Age: 18

createUser({ name: "Charlie" }); 
// 輸出:
// Name: Charlie
// Age: 18

閱讀順序詳解

步驟 1:理解函式定義

  • 開始閱讀函式的定義:function createUser(...) { ... }

步驟 2:分析參數列表

  • 注意到參數列表中的結構:({ name = "Guest", age = 18 } = {})

步驟 3:外層參數預設值

  • = {}:為參數提供預設值 {},確保在沒有傳入引數時,參數不會是 undefined

步驟 4:解構賦值提取屬性

  • { name = "Guest", age = 18 }:從參數物件中解構出 nameage 屬性。

步驟 5:屬性預設值

  • 如果物件中缺少 nameage,則使用各自的預設值:
  • name 預設為 "Guest"
  • age 預設為 18

視覺化流程圖

函式 createUser 接受參數 ({ name = "Guest", age = 18 } = {})

當函式被呼叫時:

1. 檢查是否有傳入引數:
   - 如果有,參數物件 = 傳入的引數物件
   - 如果沒有,參數物件 = {}

2. 解構參數物件,提取 name 和 age:
   - 如果物件中有 name,使用物件中的值
   - 如果物件中沒有 name,name = "Guest"
   - 如果物件中有 age,使用物件中的值
   - 如果物件中沒有 age,age = 18

3. 在函式內容中使用 name 和 age 變數

函式調用時的行為

情況 1:沒有傳入引數

createUser();
  • 步驟
  1. 沒有傳入引數,參數預設為 {}(因為 = {})。
  2. 解構 {},嘗試提取 nameage
  3. {} 中沒有 nameage,所以使用預設值:
    • name = "Guest"
    • age = 18
  4. 函式內部使用這些值。

情況 2:傳入部分屬性的物件

createUser({ name: "Charlie" });
  • 步驟
  1. 傳入的引數是 { name: "Charlie" }
  2. 解構 { name: "Charlie" },提取:
    • name = "Charlie"
    • age 不存在,使用預設值 18
  3. 函式內部使用 name = "Charlie"age = 18

情況 3:傳入完整的物件

createUser({ name: "Alice", age: 25 });
  • 步驟
  1. 傳入的引數是 { name: "Alice", age: 25 }
  2. 解構提取:
    • name = "Alice"
    • age = 25
  3. 函式內部使用這些值。

為什麼需要 = {}

  • 避免錯誤:如果沒有 = {},當沒有傳入引數時,參數為 undefined,解構 undefined 會導致錯誤。
  TypeError: Cannot destructure property 'name' of 'undefined' as it is undefined.
  • 提供安全的預設值= {} 確保參數至少是一個空物件 {},因此可以安全地進行解構賦值。

最佳實踐

清晰地命名參數

  • 意義明確:為參數取有意義的名稱,增加程式碼的可讀性。
  • 避免混淆:特別是在函式接受多個引數時,清晰的參數名稱可以避免誤解。

使用剩餘參數處理不定數量的引數

  • 彈性:當函式需要接受可變數量的引數時,使用剩餘參數。
  • 避免使用 argumentsarguments 是類陣列物件,不如剩餘參數方便。

避免過多的參數

  • 簡化函式:函式的參數過多可能表示函式的職責過於複雜,考慮拆分。
  • 使用物件作為參數:當需要傳遞多個相關的引數時,可以使用物件。

範例:

function createUser({ name, age, email }) {
  // ...
}

createUser({ name: "Dave", age: 30, email: "[email protected]" });

結語

理解 JavaScript 中的參數引數,以及它們之間的關係,對於撰寫清晰、有效的程式碼至關重要。透過掌握這些概念,你可以:

  • 正確地定義和調用函式:避免常見的錯誤。
  • 提升代碼的可讀性和可維護性:清晰的參數和引數使用使程式碼更易於理解。
  • 靈活地處理不定數量的引數:使用剩餘參數和預設值,增加函式的彈性。

希望這篇文章能夠幫助你深入了解 JavaScript 中的參數與引數,並在實際開發中靈活運用。


參考資料

Similar Posts