JavaScript 課程 – 運算子(Operators)
主題:解構賦值(Destructuring)
簡介
在日常的前端開發中,我們常常需要從物件或陣列中取出特定的屬性、元素,然後再進行後續的處理。傳統的寫法往往需要多行的變數宣告與索引或屬性存取,程式碼既冗長又不易閱讀。ES6 引入的 解構賦值(Destructuring),讓我們可以在一行之內同時完成「取值」與「變數宣告」的工作,大幅提升程式碼的簡潔度與可維護性。
解構賦值不只是一個語法糖,它同時也是一種資料抽象的思考方式。透過它,我們可以更自然地描述「我只關心資料的哪一部份」;在大型專案裡,這種寫法能減少不必要的重複、降低錯誤率,並且讓程式的意圖更加明確。
本篇文章將從基礎概念出發,逐步帶領讀者了解陣列與物件的解構寫法、進階技巧(預設值、別名、剩餘元素),並提供實務範例、常見陷阱與最佳實踐,讓你在真實專案中能夠自信地運用解構賦值。
核心概念
1. 陣列解構(Array Destructuring)
陣列解構的語法形式為:
const [a, b, c] = array;
左側的方括號代表「依序取出」陣列中的元素,依次賦值給 a、b、c。若陣列長度不足,對應的變數會得到 undefined。
範例 1:基本陣列解構
const numbers = [10, 20, 30];
// 直接解構
const [first, second, third] = numbers;
console.log(first); // 10
console.log(second); // 20
console.log(third); // 30
範例 2:跳過不需要的元素
const colors = ['red', 'green', 'blue', 'yellow'];
// 只取第一與第三個顏色
const [primary, , tertiary] = colors;
console.log(primary); // 'red'
console.log(tertiary); // 'blue'
範例 3:預設值(Default Values)
const data = [5];
// 若陣列中沒有對應位置,使用預設值
const [x = 0, y = 0] = data;
console.log(x); // 5
console.log(y); // 0 (使用預設值)
範例 4:剩餘元素(Rest Element)
const scores = [95, 87, 78, 66, 54];
// 前兩名單獨取出,剩下的放入陣列 `others`
const [gold, silver, ...others] = scores;
console.log(gold); // 95
console.log(silver); // 87
console.log(others); // [78, 66, 54]
2. 物件解構(Object Destructuring)
物件解構使用大括號 {},根據 屬性名稱 來取值:
const {propA, propB} = obj;
若屬性不存在,對應變數會是 undefined。
範例 5:基本物件解構
const user = {
name: 'Alice',
age: 28,
email: 'alice@example.com'
};
const {name, age} = user;
console.log(name); // 'Alice'
console.log(age); // 28
範例 6:屬性別名(Alias)
const point = { x: 10, y: 20 };
// 把 x、y 重新命名為 cx、cy
const { x: cx, y: cy } = point;
console.log(cx); // 10
console.log(cy); // 20
範例 7:預設值 + 別名
const options = { timeout: 5000 };
// 若沒有傳入 `retry`,使用預設值 3
const { timeout, retry = 3, mode: executionMode = 'async' } = options;
console.log(timeout); // 5000
console.log(retry); // 3
console.log(executionMode); // 'async'
範例 8:剩餘屬性(Rest Properties)
const article = {
title: '解構賦值入門',
author: 'Bob',
date: '2025-11-19',
tags: ['JavaScript', 'ES6']
};
// 把 title 取出,其他屬性放入 `meta`
const { title, ...meta } = article;
console.log(title); // '解構賦值入門'
console.log(meta); // { author: 'Bob', date: '2025-11-19', tags: [...] }
3. 混合解構(Nested Destructuring)
解構可以同時作用於巢狀的陣列或物件,讓取值更為直接。
範例 9:巢狀陣列與物件
const response = {
status: 200,
data: [
{ id: 1, name: 'Tom' },
{ id: 2, name: 'Jerry' }
],
meta: {
page: 1,
size: 2
}
};
// 只取出第一筆資料的 name,及 meta.page
const {
data: [{ name: firstName }],
meta: { page }
} = response;
console.log(firstName); // 'Tom'
console.log(page); // 1
4. 解構在函式參數中的應用
解構不僅能在變數宣告時使用,還可以直接寫在 函式參數 中,讓呼叫者只需要提供必要的屬性。
範例 10:函式參數解構
function createUser({ name, age, isAdmin = false }) {
return {
name,
age,
role: isAdmin ? 'admin' : 'user'
};
}
const newUser = createUser({ name: 'Carol', age: 31 });
console.log(newUser); // { name: 'Carol', age: 31, role: 'user' }
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方案 |
|---|---|---|
| 解構的變數名稱與屬性不一致 | 若寫成 const {name} = obj;,但物件屬性實際是 fullName,會得到 undefined。 |
使用 別名(const {fullName: name})或確認屬性名稱正確。 |
預設值僅在 undefined 時生效 |
若屬性值為 null、0、'',預設值不會被套用。 |
需要額外判斷或使用 ??(Nullish Coalescing)運算子。 |
| 剩餘元素只能放最後 | ...rest 必須是解構模式的最後一個項目。 |
確保語法位置正確,或拆成兩段解構。 |
| 解構的左側不能有副作用 | ({ a } = getObject()) 會先執行 getObject(),若函式拋錯會中斷。 |
在解構前先檢查或使用 try…catch。 |
| 陣列與物件混用時容易混淆 | const [a, {b}] = arr; 需要陣列裡的第二個元素是物件。 |
確認資料結構,或先使用 Array.isArray、typeof 檢查。 |
最佳實踐
- 只解構需要的欄位:避免一次解構全部,減少不必要的記憶體占用。
- 保持解構的可讀性:過度深層的巢狀解構會讓程式難以理解,適度拆解成多行。
- 配合預設值與型別檢查:在 API 回傳不確定的情況下,使用預設值或
??防止undefined帶來的 bug。 - 使用別名提升語意:當屬性名稱過於抽象或與局部變數衝突時,使用別名讓程式更具語意。
- 在函式參數中解構時,提供預設物件:防止呼叫者傳入
undefined而拋錯。
function fetchData({ url, method = 'GET' } = {}) {
// 若呼叫時不傳參數,仍能安全執行
}
實際應用場景
1. 前端 UI 狀態管理(如 Redux、Vuex)
解構常用於 reducer 或 mutation 中,直接取出 action.payload 裡的屬性:
function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO': {
const { id, text } = action.payload;
return [...state, { id, text, completed: false }];
}
// ...
}
}
2. API 回傳資料的快速過濾
後端常回傳大量欄位,前端只需要其中幾個:
async function loadProfile(userId) {
const response = await fetch(`/api/users/${userId}`);
const { name, avatar, bio } = await response.json(); // 只取需要的欄位
renderProfile({ name, avatar, bio });
}
3. 表單資料的解構與預設
在 React 中,onChange 事件常會傳入 event.target,使用解構可減少重複寫法:
function handleInputChange({ target: { name, value } }) {
setForm(prev => ({ ...prev, [name]: value }));
}
4. 迭代與資料轉換
配合 Array.map、Array.filter 等高階函式,解構讓回傳的結構更直觀:
const users = [
{ id: 1, profile: { name: 'Ann', age: 24 } },
{ id: 2, profile: { name: 'Ben', age: 30 } }
];
// 只取出 name 與 age
const simpleList = users.map(({ profile: { name, age } }) => ({ name, age }));
5. Node.js 設定檔與環境變數
// config.js
const { DB_HOST = 'localhost', DB_PORT = 3306 } = process.env;
module.exports = { DB_HOST, DB_PORT };
總結
解構賦值是 ES6 為 JavaScript 帶來的強大語法糖,它不僅能讓程式碼更簡潔、更具可讀性,也鼓勵開發者以「只取所需」的思考模式來設計資料流。
本文從 陣列解構、物件解構、巢狀解構、以及 函式參數解構 四個面向,提供了 10 個實作範例,說明了預設值、別名、剩餘元素等進階技巧。接著列出常見的陷阱與對應的最佳實踐,最後以 Redux、API 回傳、React 表單、資料轉換與 Node.js 設定等實務情境示範,讓讀者能夠在真實專案中立即上手。
關鍵要點
- 使用解構時,務必確認資料結構與變數名稱的對應。
- 預設值只在
undefined時生效,必要時結合??。- 過度深層的解構會降低可讀性,適度拆解或使用中介變數。
- 在函式參數中加入預設空物件,可避免傳入
undefined的錯誤。
掌握解構賦值,你的 JavaScript 程式碼將更具表達力、更易維護,也能在團隊合作中減少溝通成本。快把今天學到的技巧套用到你的下一個專案吧! 🚀