JavaScript ES6+ 新特性:解構賦值 (Destructuring Assignment)
簡介
在 ES6 之前,從物件或陣列中取出特定值往往需要寫多行冗長的程式碼,例如使用 obj.prop 或 arr[0] 逐一取值。解構賦值(Destructuring)則提供了一種宣告式的寫法,讓我們可以在同一行同時把多個屬性或元素「解開」並賦值給變數。
這項語法不僅讓程式碼更簡潔、更具可讀性,也在 React、Node.js、Vue 等現代框架中成為日常開發的必備工具。掌握解構的細節,能讓你在處理 API 回傳資料、函式參數、以及模組匯入時,寫出更安全、可維護的程式。
核心概念
1. 陣列解構
陣列解構的語法形如 let [a, b] = array;,左側的方括號列出「目標變數」的順序,右側則是來源陣列。
// 範例 1:基本陣列解構
const numbers = [10, 20, 30];
let [first, second] = numbers;
console.log(first); // 10
console.log(second); // 20
- 跳過元素:使用逗號保留位置即可。
let [, , third] = numbers; // 直接取得第三個元素
console.log(third); // 30
- 設定預設值:當來源陣列缺少對應位置時,可提供備援值。
let [x = 1, y = 2, z = 3] = [5];
console.log(x, y, z); // 5 2 3
2. 物件解構
物件解構使用大括號 {},左側列出欲取出的屬性名稱,右側則是來源物件。
// 範例 2:基本物件解構
const user = { name: "Alice", age: 28, city: "Taipei" };
let { name, age } = user;
console.log(name); // Alice
console.log(age); // 28
- 重新命名變數:使用
:後接新變數名。
let { name: userName, city: location } = user;
console.log(userName); // Alice
console.log(location); // Taipei
- 預設值:若屬性不存在,會使用預設值。
let { country = "Taiwan" } = user;
console.log(country); // Taiwan
3. 混合解構(陣列裡的物件、物件裡的陣列)
實務上常會碰到結構複雜的資料,例如 API 回傳的 JSON 陣列,每筆資料又是一個物件。
// 範例 3:陣列裡的物件解構
const posts = [
{ id: 1, title: "第一篇", tags: ["js", "es6"] },
{ id: 2, title: "第二篇", tags: ["node", "api"] }
];
let [{ title: firstTitle, tags: [firstTag] }, secondPost] = posts;
console.log(firstTitle); // 第一篇
console.log(firstTag); // js
console.log(secondPost.title); // 第二篇
4. 函式參數的解構
直接在函式參數位置解構,可讓呼叫端只傳需要的屬性,且避免在函式內重複寫 obj.prop。
// 範例 4:函式參數解構 + 預設值
function greet({ name = "訪客", age } = {}) {
console.log(`哈囉,${name}!您今年 ${age || "未知"} 歲。`);
}
greet({ name: "Bob", age: 35 }); // 哈囉,Bob!您今年 35 歲。
greet({ name: "Cathy" }); // 哈囉,Cathy!您今年 未知 歲。
greet(); // 哈囉,訪客!您今年 未知 歲。
5. 變數別名與剩餘元素(Rest)
- 剩餘元素:使用
...rest收集未列出的元素或屬性。
// 範例 5:剩餘元素(陣列)
let [head, ...tail] = [1, 2, 3, 4];
console.log(head); // 1
console.log(tail); // [2,3,4]
// 範例 6:剩餘屬性(物件)
let { name, ...others } = user;
console.log(name); // Alice
console.log(others); // { age: 28, city: 'Taipei' }
- 深層解構:可直接解構多層巢狀結構。
const config = {
server: { host: "localhost", port: 8080 },
db: { user: "admin", password: "secret" }
};
let {
server: { host, port },
db: { user }
} = config;
console.log(host, port, user); // localhost 8080 admin
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
| 解構時變數未宣告 | let {a} = obj; 正確;但直接寫 {a} = obj; 會被視為區塊語句。 |
必須在左側加上宣告關鍵字(let、const、var)或使用圓括號包住整個表達式。 |
預設值是 undefined |
若來源屬性值本身是 undefined,預設值不會生效。 |
使用 null 或其他明確的「缺值」判斷,或在解構前先處理資料。 |
| 名稱衝突 | 解構時重新命名變數容易與已有變數同名,造成覆寫。 | 盡量使用具語意的別名,或在較小的作用域內解構。 |
| 深層解構的可讀性 | 一行寫太多層級會降低可讀性。 | 只解構必要層級,或分段寫 const {a} = obj; const {b} = a; 以提升清晰度。 |
剩餘屬性 (...rest) 必須最後 |
...rest 只能放在最後,否則會拋出語法錯誤。 |
確保 ...rest 位於解構列表的最後一個位置。 |
最佳實踐
- 使用
const:除非需要重新指派,盡量用const讓變數保持不可變。 - 只解構必要屬性:避免一次解構過多,減少不必要的記憶體開銷。
- 配合預設值:在處理外部 API 時,為可能缺失的欄位提供預設值,可避免
undefined帶來的錯誤。 - 保持一致的風格:團隊內統一使用解構的寫法(例如參數解構或物件解構),提升程式碼一致性。
實際應用場景
- React 組件的 Props 解構
function Card({ title, description, imageUrl }) {
return (
<div className="card">
<img src={imageUrl} alt={title} />
<h3>{title}</h3>
<p>{description}</p>
</div>
);
}
將 props 直接解構,使 JSX 內部更乾淨。
- Node.js 讀取環境變數
const { DB_HOST = "localhost", DB_PORT = 3306 } = process.env;
快速取得設定,並提供預設值。
- 處理 API 回傳的 JSON
async function fetchUser(id) {
const response = await fetch(`/api/users/${id}`);
const { data: { name, email, profile: { avatar } = {} } = {} } = await response.json();
return { name, email, avatar };
}
一次解構多層結構,省去繁瑣的 response.data.profile.avatar。
- 陣列迭代時同時取得索引
const colors = ["red", "green", "blue"];
colors.forEach((color, index) => {
const [prev, next] = [colors[index - 1], colors[index + 1]];
console.log(`Current: ${color}, Prev: ${prev}, Next: ${next}`);
});
結合解構與迭代,讓程式碼更直觀。
總結
解構賦值是 ES6+ 中最具影響力的語法之一,它把 「取值」 的動作變成宣告式的表達,讓程式碼更簡潔、可讀且易於維護。從基本的陣列與物件解構,到函式參數、剩餘元素、深層解構,掌握這些技巧可以大幅提升開發效率,尤其在 React、Node、Vue 等現代框架中更是不可或缺。
在實務開發時,記得遵守 「只解構需要的屬性」、「使用 const」、「提供預設值」 等最佳實踐,並留意常見的語法陷阱。透過適當的解構,你的 JavaScript 程式將變得更清晰、更安全,也更符合現代前端與後端工程的編碼標準。祝你在寫程式的路上,解構出更好的人生!