在ECMAScript規範中,省略號(...)主要有兩個重要用途:展開語法(Spread syntax) 和剩餘參數(Rest parameters)。
- 淺拷貝:使用擴展運算符
...(最簡潔) - 深拷貝:使用
JSON.parse(JSON.stringify())或 Lodash 的_.cloneDeep()
快速概覽
|
語法類型
|
作用
|
使用場景
|
|
展開語法 |
將數組/對象展開為單個元素
|
函數調用、數組字面量、對象字面量
|
|
剩餘參數 |
將多個元素收集為數組/對象
|
函數參數、數組解構、對象解構
|
詳細解析
1. 展開語法 (Spread Syntax)
數組展開
// 基本用法
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5, 6];
console.log(arr2); // [1, 2, 3, 4, 5, 6]
// 函數參數傳遞
const numbers = [1, 2, 3];
console.log(Math.max(...numbers)); // 3
// 複製數組(淺拷貝)
const original = [1, 2, 3];
const copy = [...original];
對象展開
// 合併對象
const obj1 = { a: 1, b: 2 };
const obj2 = { c: 3, d: 4 };
const merged = { ...obj1, ...obj2 };
console.log(merged); // { a: 1, b: 2, c: 3, d: 4 }
// 對象淺拷貝
const originalObj = { name: "John", age: 30 };
const copyObj = { ...originalObj };
// 覆蓋屬性
const base = { color: "red", size: "large" };
const updated = { ...base, color: "blue" };
console.log(updated); // { color: "blue", size: "large" }
2. 剩餘參數 (Rest Parameters)
函數參數
// 收集剩餘參數為數組
function sum(...numbers) {
return numbers.reduce((total, num) => total + num, 0);
}
console.log(sum(1, 2, 3, 4)); // 10
// 與其他參數結合使用
function greet(greeting, ...names) {
return names.map(name => `${greeting}, ${name}!`);
}
console.log(greet("Hello", "Alice", "Bob", "Charlie"));
// ["Hello, Alice!", "Hello, Bob!", "Hello, Charlie!"]
數組解構
// 提取前幾個元素,剩餘元素收集到新數組
const [first, second, ...rest] = [1, 2, 3, 4, 5];
console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4, 5]
// 跳過元素
const [head, , ...tail] = [1, 2, 3, 4, 5];
console.log(head); // 1
console.log(tail); // [3, 4, 5]
對象解構
// 提取特定屬性,剩餘屬性收集到新對象
const person = { name: "Alice", age: 25, city: "New York", country: "USA" };
const { name, age, ...address } = person;
console.log(name); // "Alice"
console.log(age); // 25
console.log(address); // { city: "New York", country: "USA" }
實際應用場景
1. 函數參數處理
// 替代 arguments 對象
function logMessages(...messages) {
messages.forEach(message => console.log(message));
}
logMessages("Error:", "File not found", "Code: 404");
// 靈活的配置函數
function createElement(tag, { className, id, ...attrs } = {}) {
const element = document.createElement(tag);
if (className) element.className = className;
if (id) element.id = id;
// 設置其他屬性
Object.keys(attrs).forEach(key => {
element.setAttribute(key, attrs[key]);
});
return element;
}
2. 數據轉換和處理
// 數組合並和去重
const arr1 = [1, 2, 3];
const arr2 = [2, 3, 4];
const mergedUnique = [...new Set([...arr1, ...arr2])];
console.log(mergedUnique); // [1, 2, 3, 4]
// 對象屬性過濾
const user = {
name: "John",
password: "secret",
email: "john@example.com",
age: 30
};
// 移除敏感信息
const { password, ...publicProfile } = user;
console.log(publicProfile);
// { name: "John", email: "john@example.com", age: 30 }
3. React中的應用
// Props傳遞
function Button({ variant, size, ...props }) {
return (
<button
className={`btn btn-${variant} btn-${size}`}
{...props} // 傳遞所有其他props(onClick, disabled等)
/>
);
}
// 狀態更新
const [state, setState] = useState({ count: 0, theme: 'dark' });
function updateTheme(newTheme) {
setState(prevState => ({
...prevState, // 保留其他狀態
theme: newTheme
}));
}
注意事項
1. 淺拷貝問題
const nestedArray = [[1], [2], [3]];
const shallowCopy = [...nestedArray];
shallowCopy[0].push(99);
console.log(nestedArray[0]); // [1, 99] - 原數組也被修改了
const nestedObj = { user: { name: "John" } };
const shallowObjCopy = { ...nestedObj };
shallowObjCopy.user.name = "Jane";
console.log(nestedObj.user.name); // "Jane" - 原對象也被修改了
2. 使用限制
// 剩餘元素必須是最後一個
// ❌ 錯誤寫法
// const [...rest, last] = [1, 2, 3, 4];
// ✅ 正確寫法
const [first, ...rest] = [1, 2, 3, 4];
// 對象中只能有一個剩餘參數
// ❌ 錯誤寫法
// const { ...rest, ...more } = { a: 1, b: 2 };
3. 瀏覽器兼容性
// 檢查支持情況
const supportsSpread = () => {
try {
eval("[...[1,2,3]]");
return true;
} catch (e) {
return false;
}
};
console.log("Spread syntax supported:", supportsSpread());
總結
ES規範中的省略號提供了強大而靈活的數據處理能力:
- 展開語法:用於"展開"可迭代對象,適合數據合併、函數調用等場景
- 剩餘參數:用於"收集"多個元素,適合參數處理、解構賦值等場景
這兩種語法大大簡化了JavaScript代碼,提高了開發效率和代碼可讀性。
本文章為轉載內容,我們尊重原作者對文章享有的著作權。如有內容錯誤或侵權問題,歡迎原作者聯繫我們進行內容更正或刪除文章。