JavaScript 初學者必懂:解構賦值 vs 展開運算子,傻傻分不清?一次搞懂!

更新日期: 2025 年 3 月 31 日

在 JavaScript 中,有兩個非常常見又容易混淆的語法:解構賦值(Destructuring Assignment)展開運算子(Spread Operator)

初學者常常一看到 ... 就一頭霧水:「這到底是拆東西還是複製東西?」

更糟的是,這兩者都會用到花括號 {} 或中括號 [],還都能操作物件和陣列,根本就像雙胞胎一樣!

但別擔心,這篇文章會帶你一次分清楚兩者的語法位置、功能差異與使用時機!


基本概念解析:語法相似,用法卻完全不同!

雖然「解構賦值(Destructuring)」和「展開運算子(Spread)」都會使用 ...,也常出現在物件與陣列操作中,但它們的本質與用途是完全不同的

我們來從四個角度深入比較,幫你一眼看懂差在哪:

目的不同:到底是「拆資料」還是「複製資料」?

項目解構賦值 (Destructuring)展開運算子 (Spread)
用途把資料「拆開來」,指派給變數使用把整包資料「複製/展開」,產生一個新結構

解構賦值是用來從陣列或物件中「取出特定的欄位或元素」,然後把它們分別指派給變數。這很適合在你只需要部分資料時使用。

例如:

const { name } = person;

這就是從 person 裡面拆出 name

展開運算子則是把整份物件或陣列「完整展開」,放入新的結構中,像是建立副本、新的物件或合併資料。

例如:

const copy = { ...person };

這是把整個 person 複製一份出來。

語法位置不同:看 = 左邊還是右邊就知道

項目解構賦值 (Destructuring)展開運算子 (Spread)
出現位置等號「左邊」等號「右邊」

這是判斷兩者最簡單的方法之一!

  • 如果你看到 ... 出現在 等號左邊,那通常是解構賦值中的剩餘參數(rest)語法
  • 如果你看到 ... 出現在 等號右邊,那就是展開運算子,代表要展開某個結構。

🔍 範例比較

// 解構(= 左邊)
const [first, ...rest] = arr;

// 展開(= 右邊)
const newArr = [...arr];

都會用到 ...,但意思不一樣!

項目解構賦值 (Destructuring)展開運算子 (Spread)
是否用到 ...?✅ 有,但是「收集剩下的」✅ 有,用來「展開所有內容」

雖然兩者都會看到 ...,但背後的意思不同:

  • 在解構中,... 是用來「收集剩下的資料」,稱作 rest parameter。 例:
const [a, b, ...rest] = [1, 2, 3, 4];
console.log(rest); // [3, 4]
  • 在展開中,... 是用來「展開原始資料」,直接插入到新的結構中。 例:
const arr = [1, 2];
const newArr = [...arr, 3];
console.log(newArr); // [1, 2, 3]

都不會改原資料:操作安全又放心

項目解構賦值 (Destructuring)展開運算子 (Spread)
會改原資料嗎?❌ 不會改❌ 不會改(除非後續手動改)

無論你是用解構還是展開,原始的物件或陣列都不會被動到

這就是 JavaScript 的「非破壞性操作(non-destructive)」概念,很重要!

🔍 範例驗證

const obj = { a: 1, b: 2 };

const { a } = obj;
const copy = { ...obj, b: 3 };

console.log(obj); // 仍然是 { a: 1, b: 2 }

小抄:一張表一次搞懂

比較項目解構賦值 (Destructuring)展開運算子 (Spread)
用途拆資料、取值給變數展開資料、複製用
語法位置= 左邊= 右邊
是否會用 ...✅ 有(收集剩下的值)✅ 有(展開全部內容)
會不會改原資料?❌ 不會❌ 不會(除非你後續手動改)

想要記得更牢嗎?這句話送給你當記憶口訣:

🧠 「解構是拆出幾個變數來用;展開是整包複製再加工!」

接下來,我們會透過更多範例幫你鞏固這些知識!


解構賦值(Destructuring Assignment)是什麼?

👉 用途簡介:把資料從物件或陣列中「拆出來」,變成你要用的變數

在 JavaScript 中,解構賦值是一種語法糖(syntactic sugar),讓你可以快速從物件或陣列中提取資料,並直接把這些資料賦值給變數

這種寫法讓你的程式碼更簡潔,也更直覺。

這就像是:你收到一個禮盒(物件或陣列),裡面裝了很多東西,但你只想要裡面特定幾樣。

解構賦值就能幫你打開禮盒,把想要的東西一一取出,變成你自己的變數來使用。

物件解構:從物件中拆值

const person = { name: 'Alice', age: 25 };

const { name, age } = person;

console.log(name); // 'Alice'
console.log(age);  // 25

🧠 這段在做什麼?

  • person 是一個物件,裡面有兩個屬性:nameage
  • { name, age } = person 這行程式的意思是:
    • person.name 的值賦給變數 name
    • person.age 的值賦給變數 age

原本你可能要這樣寫:

const name = person.name;
const age = person.age;

但用了解構賦值後,只需要一行就搞定,程式碼更簡潔。

重點是:你只是在取值,不會改變原本的 person

陣列解構:從陣列中拆值

const numbers = [1, 2, 3, 4, 5];

const [first, second] = numbers;

console.log(first);  // 1
console.log(second); // 2

🧠 這段在做什麼?

  • 陣列解構是根據「位置順序」來對應的。
  • 第一個元素 1 被賦值給 first,第二個元素 2 被賦值給 second
  • 它不像物件解構是用「鍵名」,而是看「位置」。

陣列解構 + ...:搭配「剩餘參數(rest)」語法

const numbers = [1, 2, 3, 4, 5];

const [first, second, ...rest] = numbers;

console.log(first);  // 1
console.log(second); // 2
console.log(rest);   // [3, 4, 5]

🧠 這段又多做了什麼?

這裡多了一個 ...rest,這是解構賦值裡面一個很重要也容易混淆的進階用法。

  • first 會拿到第一個值(1)
  • second 拿到第二個值(2)
  • rest 則是「剩下的所有元素」組成的新陣列 [3, 4, 5]

這種寫法的用途很多,比如:

  • 分開處理前幾個元素與其餘元素
  • 在函式中只關心第一個參數,其他參數包成陣列
  • 撰寫更具彈性的資料處理邏輯

🚫 常見誤會澄清:這裡的 ...rest 不是展開運算子!

很多人一看到 ... 就以為是「展開運算子(spread)」,但事實上:

  • 解構賦值中 使用 ...,它的角色是「剩餘參數(rest parameter)」。
  • 建立新物件或陣列時 使用 ...,它的角色才是「展開運算子(spread operator)」。

這兩種語法雖然長得一樣,但用途與語境完全不同!

...rest...arr
解構時使用:收集剩下的值結構定義時使用:展開所有值
出現在 = 左邊出現在 = 右邊
屬於解構語法屬於展開語法

小結:什麼情況適合用解構?

  • 只想要部分資料,不想打完整的 obj.keyarr[0]
  • 想要讓程式碼更乾淨、變數定義更明確
  • 想取出特定欄位或陣列前幾項進行運算

🔑 記住一句話:

「我只要某幾項,請幫我拆出來用」= 解構賦值

接下來,我們會進入「展開運算子」的世界,看看整份資料要怎麼優雅地複製與組合!


展開運算子(Spread Operator)是什麼?

👉 用途總覽:把整個資料「展開/複製」到新陣列或新物件中

展開運算子(... 是 JavaScript 中非常實用的語法,主要用途是:

  • 展開 陣列或物件的內容
  • 複製 一份資料,製作副本(通常是淺層複製)
  • 合併 多個物件或陣列
  • 增加/覆蓋屬性或元素

展開運算子可以讓你的資料操作更「乾淨」且「非破壞性」,也就是不會改到原資料,讓程式碼更安全、彈性更高。

基本語法結構

// 展開陣列
const newArr = [...oldArr];

// 展開物件
const newObj = { ...oldObj };
  • 在這裡的 ... 是用在 等號右邊,代表「展開某個資料結構的內容」。
  • 你可以把展開的內容,加入新資料、新屬性,甚至在某些情況下覆蓋舊值。

物件展開

const person = { name: 'Alice', age: 25 };

const newPerson = {
  ...person,
  age: 30,        // 覆蓋原本的 age
  city: 'Taipei'  // 新增 city 屬性
};

console.log(newPerson);
// ➜ { name: 'Alice', age: 30, city: 'Taipei' }

🧠 這段程式在做什麼?

  1. ...personperson 裡面的所有 key-value 組合(name: 'Alice', age: 25)展開出來。
  2. 接著你「覆蓋」了 age(從 25 變成 30),並且「新增」了一個新的屬性 city
  3. 最後得到的新物件 newPerson 包含了這些變更。

原本的 person 完全沒有被更動,這就是展開運算子的非破壞性特質。

陣列展開

const numbers = [1, 2, 3];

const newNumbers = [...numbers, 4, 5];

console.log(newNumbers);
// ➜ [1, 2, 3, 4, 5]

🧠 這段程式在做什麼?

  1. ...numbers 把陣列 [1, 2, 3] 展開為三個獨立的值:1, 2, 3
  2. 接著在這些值後面,加入新的元素 45
  3. 結果是產生一個新的陣列 newNumbers,包含所有舊的加上新的元素。

✅ 原本的 numbers 還是 [1, 2, 3],不會被改變。

陣列合併:快速合併多個陣列

const a = [1, 2];
const b = [3, 4];
const c = [...a, ...b];

console.log(c); // ➜ [1, 2, 3, 4]

這種展開方式比傳統的 .concat() 更簡潔直觀,是現代 JavaScript 中最推薦的陣列合併寫法。

物件合併與屬性覆蓋

const base = { a: 1, b: 2 };
const override = { b: 99, c: 100 };

const combined = { ...base, ...override };

console.log(combined); // ➜ { a: 1, b: 99, c: 100 }

注意:後面出現的屬性會覆蓋前面的屬性。這也是常見用法,例如覆寫預設設定。

展開 vs 解構:... 長得一樣,用法不一樣!

比較項目展開運算子(Spread)解構賦值中的剩餘語法(Rest)
功能展開整個資料(複製、合併)收集剩下的資料
出現位置等號右邊等號左邊
常見應用製作副本、新增屬性取得剩下的元素
範例{ ...obj }, [...arr]const { a, ...rest } = obj

記住:兩者的 ... 用法是不同角色,不可混為一談!

小結:什麼時候用展開運算子?

  • 想要複製陣列或物件,避免直接修改原始資料(避免副作用)
  • 想要在複製的同時加料或改值
  • 想要合併多份資料結構(如:設定檔、參數組)

🔑 記憶口訣:

「解構是取值,展開是複製」

「一個是拆開用,一個是整包搬!」


結語:不是看語法,是看「意圖」

這兩種語法乍看很像,真正的差異在於「你想幹嘛」:

  • 想從資料中「取出幾個欄位來用」?👉 解構賦值
  • 想要「複製一份資料來加工」?👉 展開運算子

只要記住這個意圖,配合語法位置,就能不再混淆這對雙胞胎語法啦!

Similar Posts