跳至主要内容

本章為 JavaScript 核心重構的第六天,將帶領你破解陣列與物件的傳址詛咒。透過「爆破影印術」的比喻,解析如何運用展開運算子 (...) 瞬間斷開與原版資料的靈魂連結。本文除了介紹資料合併與覆蓋的實戰技巧,更進一步探討淺拷貝 (Shallow Copy) 與深拷貝 (Deep Copy) 的差異及陷阱,穩固高階開發中資料不可變性 (Immutability) 的實作基礎。

Day 06: 原理篇 —— 影印術!展開運算子 ...

本文同步分享於2026鐵人賽:JS 核心重構:勇者轉職傳說

📜 這裡的修煉規則

孩子,在踏上今日的領地前,先坐下來喝杯酒吧。 修煉的路很長,別急著奔跑,先聽聽這份卷軸裡的古老叮嚀。

我在每一站的盡頭都為你鋪設了 實戰演武場 CodePen。那不只是程式碼的拼湊,而是你在這場暴風雨中,試圖用邏輯點燃的一顆星火。 成功突圍後,別忘了帶著你的戰利品回到 QuestBoard 佈告欄。在那裡,你會發現自己從不孤單,公會的夥伴們都正舉杯等待你的歸來。

記住...

導師看重的從來不是你的劍法有多華麗,而是當你被邏輯擊潰、滿身泥濘後,依然選擇握緊理性的劍柄,再次向真相發起衝鋒的勇氣。

💡 【導師講義:底層真相探究】

1. 打破容器的「爆破影印術」

展開運算子 ... 的動作極其簡單:它就是把裝在「陣列」或「物件」裡的舊物資全部「倒出來」,放在空中供你重新運用。

app.js
const oldScrolls = ["火球術", "治癒術"];

// ✅ 影印術:變出一個新木箱 [],再把舊捲軸 ... 倒進去
const newScrolls = [...oldScrolls, "傳送術"];

console.log(newScrolls); // ["火球術", "治癒術", "傳送術"]
console.log(oldScrolls); // ["火球術", "治癒術"] (原版毫髮無傷,連結已斷開!)

2. 資料重組的「合併與覆蓋」

影印術最強的地方在於,你可以在合併的瞬間,對資料進行「進化」。

app.js
const baseSkill = { category: "魔法", mp: 10 };
const fireball = { name: "火球術", mp: 50 };

// ✅ 兩組合併:後面的內容會蓋掉前面的内容 (Overwriting)
// 就像是在新箱子裡先放舊零件,再把改良零件丟進去,重複的會被替換。
const superFireball = {
...baseSkill,
...fireball,
level: "Max",
};

console.log(superFireball);
// { category: "魔法", name: "火球術", mp: 50, level: "Max" }
🏹 導師的辨析圖:影印與連結
修煉招式標籤賦值 (=)展開影印 (...)
核心結果共享同一把鑰匙打造全新實體
修改副作用A 改,B 跟著變各走各路,互不干擾
記憶體狀態指向同一個地址產生獨立新地址

3. 邏輯流向圖解

💡 導師講評:圖解心法

想像這是一個 「影印現場」。展開運算子就像是一台快速影印機,它把大箱子(物件)的殼印了一份,但裡面的貴重保險箱(嵌套物件)依然只有一張 「同樣的提貨單」。而深拷貝則是直接派人去另一個城市,重新打造了一個完全一模一樣、且內外都獨立的新保箱。

影印術邏輯圖

⚔️ 【戰術對抗:學長與學弟的代碼對決】

招式示範:小隊集結 (陣列合併)

❌ 冒險學弟:吃力的「手動接龍」

  • 負能量評級:🔴 寫法生硬 / 🔴 難以閱讀
app.js
const teamA = ["戰士", "法師"];
const teamB = ["牧師", "弓箭手"];

// 雖然能用 concat,但在多組數據合併時會變得很混亂
const fullTeam = teamA.concat(teamB);

✅ 勇者學長:直覺的「空投合併」

  • 利潤評級:🟢 極致優雅 / 🟢 一目瞭然
app.js
const teamA = ["戰士", "法師"];
const teamB = ["牧師", "弓箭手"];

// 像把兩個麻袋直接打開倒進大麻袋一樣簡單
const fullTeam = [...teamA, ...teamB];
💡 導師講評

學長的寫法就像是在戰場上同時召喚兩支軍隊,你一眼就能看出最終的「大名單」長什麼樣子。這就是程式碼的「描述力」。

🏰 【勇者精英課:邁向職人的進階架構】

1. 影印機的極限:淺拷貝 (Shallow Copy)

孩子,別以為 ... 是無敵的。它雖然會影印大箱子,但如果箱子裡還裝著 「另一個小箱子」,影印機印出來的依然是 同一把小箱子的鑰匙

app.js
const hero = { name: "辛梅爾", bag: { coin: 10 } };
const clone = { ...hero };

clone.bag.coin = 999;
console.log(hero.bag.coin); // 999 (救命!裡層物件還是連著的!)

當你處理「多層嵌套」的物資時,請記得:展開運算子只管第一層。

2. 終極影印:深拷貝 (Deep Copy)

當你的寶箱裡疊了好幾層箱子(嵌套物件)時,我們需要施展更徹底的「次元斷開術」。

  • 招式 A:次元切割術 (JSON.parse/stringify) 這是公會裡最古老也最常用的招式。我們先將整個物件轉化為 「無靈魂的字串 (JSON)」,再將字串重新生成為一個 「全新的物件」

    app.js
    const hero1 = { name: "勇者", bag: { coin: 10 } };
    const hero2 = JSON.parse(JSON.stringify(hero1)); // ✂️ 徹底斷開!

    hero2.bag.coin = 999;
    console.log(hero1.bag.coin); // 10 (成功守衛!第一、二層都不再連動)
    ⚠️ 實戰雷區:避開那些致命陷阱

    此招雖然強大,但它會遺忘物件裡的「魔法招式 (Function)」與「特殊印記 (Symbol)」。

  • 招式 B:高階影印機 (structuredClone()) 這是近年來公會引進的最高階武器。它能無損複製循環引用與特殊數據類型,但它極度排斥 「魔法函式 (Function)」

    ⚠️ 實戰雷區:避開那些致命陷阱

    如果你的物件裡藏著咒語(函式),發動 structuredClone() 會導致 「次元崩潰 (DataCloneError)」。這就是為什麼資深前輩會說:「深拷貝資料時,請確保裡面只裝著純粹的物資,不要裝著會動的靈魂。」

3. 其餘參數 (Rest Parameters):剩下的都進口袋!

除了「倒出來」,... 還能當作 「萬能麻袋」,把它放在函式的接收處,它能捕捉所有剩餘的資料。

app.js
function captureLoot(firstGold, ...others) {
console.log(`金幣:${firstGold}`);
console.log(`其餘雜物:`, others); // others 自動變成陣列
}
🛖 營火叮嚀:導師的經驗談

孩子,導師當年維護「世界地圖物件」時,因為不懂 ... 的重要性,直接 const newMap = currentMap; 就去修改地圖上的魔王城位置。

結果那一秒,全伺服器的冒險者看到的地圖都亂了套,原本的新手村旁邊突然出現了魔王。那天我收到了公會歷史上最多的投訴信。

記住:「如果你打算修改某份前人傳下來的物件,請先『影印一份新的』再動手。這是身為資深勇者最基本的溫柔。」

天色微亮,營火雖已燃盡,但你眼中卻閃爍著領悟的光芒。在前往演武場挑戰關卡前,我已經幫你把靈魂碎片精煉成了這份「戰術錦囊」。拿好它,今日的任務不再是負擔,而是你證明自我的舞台。去吧,公會的英雄榜在等著你的戰報。

📝 【夥伴筆記:今日修煉精華】

這份筆記是你的隨身護身符,卡關時看一眼,真相就在裡面。

  • 語法特徵:三個點 ...,用於拆開容器。
  • 防止副作用:用 [...old]{...old} 產生新副本,斷開傳址連結。
  • 後蓋前規則:合併多個物件時,後方的相同屬性會覆蓋前方(Update 的關鍵)。
  • 萬能口袋:在參數處使用 ... 收集剩餘資料。

🎯 【實戰演武場】

⚔️ 任務鑑定條件:

  1. 完成 📜 本日實戰任務 (CodePen)
  2. 將 CodePen 網址貼至 QuestBoard,並回填鑑定報告:

⚔️ 任務鑑定條件:

  • 初心者:影印術掌握度 (成就感發掘)
    • 今天成功使用 ... 斷開影子分身的那一刻,你有沒有感受到數據終於「受你控制」的自由感?
  • 冒險者:影印機的選擇 (理性挑戰)[⚡ 觀念辨析]
    • 如果我的裝備物件 gear 裡面裝著一個「自動修復函式 (Function)」,為什麼使用 JSON.parse(JSON.stringify()) 影印後,這招魔法會消失不見?

任務完成後,你的名字將永遠標記在公會的英雄榜上!

📚 【圖書館卷軸與前後站連結】