如何深度複製 JavaScript 物件
在 JavaScript 中,有許多方法可以複製物件,但並非所有方法都提供深層複製。本文將介紹最有效的方式,並提供所有可用的選項。
2022年更新:只需使用
structuredClone()
方法進行複製。詳見如何在 JavaScript 中複製物件
在 JavaScript 中複製物件可能會很棘手。有些方法會執行淺層複製,這是大多數情況下的預設行為。
深層複製 vs 淺層複製
淺層複製能成功複製原始類型,如數字和字串,但是任何物件引用都不會被遞迴複製,而是新複製的物件將引用相同的物件。
如果物件包含其他物件的引用,在對該物件進行淺層複製時,只會複製對外部物件的引用。
而在深層複製中,這些外部物件也會被複製,因此新複製的物件與原始物件完全獨立。
當在網路上搜尋如何在 JavaScript 中深度複製物件時,你會找到很多回答,但這些回答並不總是正確的。
最簡單的選項:使用 Lodash
我建議你依賴於一個經過良好測試、非常受歡迎且精心維護的庫來執行深度複製:Lodash。
Lodash 提供了非常方便的 clone
和 cloneDeep
函數可以執行淺層和深度複製。
Lodash 還具有一個很好的特性:你可以單獨導入單個函數到你的項目中,以大大減少其依賴的大小。
在 Node.js 中:
1 | const clone = require('lodash/clone') |
以下是使用這兩個函數的示例:
1 | const clone = require('lodash/clone') |
在這個簡單的示例中,我們首先進行了淺層複製,然後編輯了 i.color
屬性,它會傳播到複製的物件中。
而在深層複製中,這種情況不會發生。
使用 Object.assign()
Object.assign()
執行的是物件的淺層複製,而非深度複製。
1 | const copied = Object.assign({}, original) |
由於它是淺層複製,它會將值複製過去,而物件引用只會被複製(而不是物件本身),所以如果你在原始物件中編輯物件屬性,複製的物件也會被修改,因為內部引用的物件是相同的:
1 | const original = { |
使用物件展開運算子
展開運算子 是一個 ES6/ES2015 功能,它提供了一種很方便的方法來執行淺層複製,相當於 Object.assign()
。
1 | const copied = { ...original } |
錯誤的解決方案
在網路上你會找到很多建議。以下是一些錯誤的解決方案:
使用 Object.create()
注意:不推薦使用
1 | const copied = Object.create(original) |
這是錯誤的,它並不會進行任何複製。
相反,original
物件被用作 copied
的 原型。
雖然它看起來可以運作,但其實不是這樣的:
1 | const original = { |
JSON序列化
注意:不推薦使用
有些人建議先將物件轉換為 JSON 格式:
1 | const cloned = JSON.parse(JSON.stringify(original)) |
但這會有一些意想不到的後果。
這樣做會丟失沒有 JSON 等效類型的 JavaScript 特性,例如Function
或Infinity
,所有值為undefined
的屬性會被JSON.stringify
忽略,從而導致它們在複製的物件中被漏掉。
同時,一些物件會被轉換為字符串,比如 Date 物件(同時,不考慮時區並默認為UTC)、Set、Map 等等:
1 | JSON.parse( |
只有在你沒有任何內部物件和函數,僅有值的情況下,此方法才能正常運作。
tags: [“javascript”, “deep clone”, “object cloning”]