JavaScript 深拷貝完整指南:掌握物件複製的關鍵技巧

在 JavaScript 開發中,正確處理物件拷貝是一個重要的技能。本文將詳細介紹各種深拷貝的方法,幫助你選擇最適合的實作方式。

深拷貝 vs 淺拷貝:關鍵差異

在開始介紹各種方法之前,我們先來理解深拷貝和淺拷貝的差異:

  • 淺拷貝:只複製第一層屬性,內部物件仍然共享參考
  • 深拷貝:遞迴複製所有層級,創建完全獨立的新物件

實作方法 1:使用 JSON 方法

最簡單的深拷貝方法是使用 JSON.parse()JSON.stringify()

1
2
3
4
5
6
7
8
9
10
11
12
// 簡單的深拷貝實作
function jsonDeepCopy(obj) {
// 將物件轉換為 JSON 字串,再解析回物件
return JSON.parse(JSON.stringify(obj));
}

// 使用範例
const original = {
name: 'John',
info: { age: 25 }
};
const copy = jsonDeepCopy(original);

優點:

  • 實作簡單
  • 適用於大多數簡單物件

缺點:

  • 無法處理函式
  • 會丟失 undefined 值
  • 無法處理循環引用
  • 無法複製特殊物件(如 Date、RegExp)

實作方法 2:遞迴實作

如果需要更完整的深拷貝功能,可以使用遞迴方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function deepCopy(obj, hash = new WeakMap()) {
// 處理 null 或非物件的情況
if (obj === null || typeof obj !== 'object') {
return obj;
}

// 處理日期物件
if (obj instanceof Date) {
return new Date(obj);
}

// 處理正則表達式
if (obj instanceof RegExp) {
return new RegExp(obj);
}

// 檢查循環引用
if (hash.has(obj)) {
return hash.get(obj);
}

// 創建新物件或陣列
const copy = Array.isArray(obj) ? [] : {};

// 將物件加入 hash 表以處理循環引用
hash.set(obj, copy);

// 遞迴複製所有屬性
Object.keys(obj).forEach(key => {
copy[key] = deepCopy(obj[key], hash);
});

return copy;
}

優點:

  • 可以處理各種複雜情況
  • 支援循環引用
  • 可以自訂複製行為

缺點:

  • 實作較複雜
  • 效能可能較差
  • 需要注意記憶體使用

實作方法 3:使用結構化克隆

使用 structuredClone() API(需要注意瀏覽器支援):

1
2
3
4
5
6
7
8
// 使用結構化克隆進行深拷貝
const original = {
name: 'John',
info: { age: 25 },
date: new Date()
};

const copy = structuredClone(original);

優點:

  • 原生 API
  • 支援大多數內建型別
  • 可以處理循環引用

缺點:

  • 不支援函式
  • 瀏覽器支援度需要確認
  • 某些特殊物件可能無法複製

實作方法 4:使用第三方函式庫

如果需要更穩定的解決方案,可以使用 Lodash 的 cloneDeep 方法:

1
2
3
// 使用 Lodash
const _ = require('lodash');
const copy = _.cloneDeep(original);

優點:

  • 穩定性高
  • 功能完整
  • 已經過充分測試

缺點:

  • 需要引入額外依賴
  • 增加專案大小
  • 可能過度依賴第三方套件

效能考量

在選擇深拷貝方法時,需要考慮以下因素:

  1. 物件大小與複雜度
  2. 效能要求
  3. 功能需求
  4. 程式碼維護性

建議使用時機

  • 簡單物件:使用 JSON 方法
  • 需要完整功能:使用遞迴實作
  • 現代瀏覽器:使用 structuredClone
  • 專案中已使用 Lodash:使用 _.cloneDeep

結論

深拷貝是 JavaScript 開發中的重要概念,選擇合適的方法需要根據實際需求來決定。建議先評估專案需求,再選擇最適合的實作方式。