本文 AI 產出,尚未審核

JavaScript 課程 – 運算子(Operators)

主題:解構賦值(Destructuring)


簡介

在日常的前端開發中,我們常常需要從物件或陣列中取出特定的屬性、元素,然後再進行後續的處理。傳統的寫法往往需要多行的變數宣告與索引或屬性存取,程式碼既冗長又不易閱讀。ES6 引入的 解構賦值(Destructuring),讓我們可以在一行之內同時完成「取值」與「變數宣告」的工作,大幅提升程式碼的簡潔度與可維護性。

解構賦值不只是一個語法糖,它同時也是一種資料抽象的思考方式。透過它,我們可以更自然地描述「我只關心資料的哪一部份」;在大型專案裡,這種寫法能減少不必要的重複、降低錯誤率,並且讓程式的意圖更加明確。

本篇文章將從基礎概念出發,逐步帶領讀者了解陣列與物件的解構寫法、進階技巧(預設值、別名、剩餘元素),並提供實務範例、常見陷阱與最佳實踐,讓你在真實專案中能夠自信地運用解構賦值。


核心概念

1. 陣列解構(Array Destructuring)

陣列解構的語法形式為:

const [a, b, c] = array;

左側的方括號代表「依序取出」陣列中的元素,依次賦值給 abc。若陣列長度不足,對應的變數會得到 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 時生效 若屬性值為 null0'',預設值不會被套用。 需要額外判斷或使用 ??(Nullish Coalescing)運算子。
剩餘元素只能放最後 ...rest 必須是解構模式的最後一個項目。 確保語法位置正確,或拆成兩段解構。
解構的左側不能有副作用 ({ a } = getObject()) 會先執行 getObject(),若函式拋錯會中斷。 在解構前先檢查或使用 try…catch
陣列與物件混用時容易混淆 const [a, {b}] = arr; 需要陣列裡的第二個元素是物件。 確認資料結構,或先使用 Array.isArraytypeof 檢查。

最佳實踐

  1. 只解構需要的欄位:避免一次解構全部,減少不必要的記憶體占用。
  2. 保持解構的可讀性:過度深層的巢狀解構會讓程式難以理解,適度拆解成多行。
  3. 配合預設值與型別檢查:在 API 回傳不確定的情況下,使用預設值或 ?? 防止 undefined 帶來的 bug。
  4. 使用別名提升語意:當屬性名稱過於抽象或與局部變數衝突時,使用別名讓程式更具語意。
  5. 在函式參數中解構時,提供預設物件:防止呼叫者傳入 undefined 而拋錯。
function fetchData({ url, method = 'GET' } = {}) {
  // 若呼叫時不傳參數,仍能安全執行
}

實際應用場景

1. 前端 UI 狀態管理(如 Redux、Vuex)

解構常用於 reducermutation 中,直接取出 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.mapArray.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 程式碼將更具表達力、更易維護,也能在團隊合作中減少溝通成本。快把今天學到的技巧套用到你的下一個專案吧! 🚀