初學者指南:深入了解 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
獲取引數的數量,遍歷所有的引數。
- 使用
執行流程詳解
- 函式調用:
- 調用
sum(1, 2, 3, 4);
,傳入引數 1、2、3、4。
- 調用
- 進入函式:
arguments
物件包含了[1, 2, 3, 4]
。arguments.length
為 4。
- 初始化
total
total
被設置為 0。
- 開始迴圈:
- 第一次迭代 (
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
。
- 第一次迭代 (
- 結束迴圈:
- 因為
i = 4
,不小於arguments.length
,迴圈結束。
- 因為
- 返回結果:
- 函式返回
total
,即 10。
- 函式返回
- 輸出結果:
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 }
:從參數物件中解構出name
和age
屬性。
步驟 5:屬性預設值
- 如果物件中缺少
name
或age
,則使用各自的預設值: 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();
- 步驟:
- 沒有傳入引數,參數預設為
{}
(因為= {}
)。 - 解構
{}
,嘗試提取name
和age
。 {}
中沒有name
和age
,所以使用預設值:name = "Guest"
age = 18
- 函式內部使用這些值。
情況 2:傳入部分屬性的物件
createUser({ name: "Charlie" });
- 步驟:
- 傳入的引數是
{ name: "Charlie" }
。 - 解構
{ name: "Charlie" }
,提取:name = "Charlie"
age
不存在,使用預設值18
- 函式內部使用
name = "Charlie"
和age = 18
。
情況 3:傳入完整的物件
createUser({ name: "Alice", age: 25 });
- 步驟:
- 傳入的引數是
{ name: "Alice", age: 25 }
。 - 解構提取:
name = "Alice"
age = 25
- 函式內部使用這些值。
為什麼需要 = {}
?
- 避免錯誤:如果沒有
= {}
,當沒有傳入引數時,參數為undefined
,解構undefined
會導致錯誤。
TypeError: Cannot destructure property 'name' of 'undefined' as it is undefined.
- 提供安全的預設值:
= {}
確保參數至少是一個空物件{}
,因此可以安全地進行解構賦值。
最佳實踐
清晰地命名參數
- 意義明確:為參數取有意義的名稱,增加程式碼的可讀性。
- 避免混淆:特別是在函式接受多個引數時,清晰的參數名稱可以避免誤解。
使用剩餘參數處理不定數量的引數
- 彈性:當函式需要接受可變數量的引數時,使用剩餘參數。
- 避免使用
arguments
:arguments
是類陣列物件,不如剩餘參數方便。
避免過多的參數
- 簡化函式:函式的參數過多可能表示函式的職責過於複雜,考慮拆分。
- 使用物件作為參數:當需要傳遞多個相關的引數時,可以使用物件。
範例:
function createUser({ name, age, email }) {
// ...
}
createUser({ name: "Dave", age: 30, email: "[email protected]" });
結語
理解 JavaScript 中的參數和引數,以及它們之間的關係,對於撰寫清晰、有效的程式碼至關重要。透過掌握這些概念,你可以:
- 正確地定義和調用函式:避免常見的錯誤。
- 提升代碼的可讀性和可維護性:清晰的參數和引數使用使程式碼更易於理解。
- 靈活地處理不定數量的引數:使用剩餘參數和預設值,增加函式的彈性。
希望這篇文章能夠幫助你深入了解 JavaScript 中的參數與引數,並在實際開發中靈活運用。