在 JavaScript 中克隆對象的 4 種方法
由於 JavaScript 對像是引用類型,所以不能只使用等號運算符 (=
) 來複製一個對象。在 JavaScript 中創建對象時,其值為 not 分配給變量的目錄。相反,該變量只保存對該值的引用。
什麼是引用類型?
讓我們看下面的例子來了解reference 類型表示:
const obj1 = { mango: '🥭️', apple: '🍎' };
const obj2 = obj1;
console.log(
obj1, // { mango: '🥭️', apple: '🍎' }
obj2 // { mango: '🥭️', apple: '🍎' }
);
正如您在上面看到的,我創建了一個對象,然後使用 =
將其分配給一個新變量 操作員。兩個對像都輸出相同的鍵值對。到目前為止,一切順利!
現在讓我們為第一個對象添加一個新鍵,看看會發生什麼:
obj1.lemon = '🍋';
console.log(
obj1, // { mango: '🥭️', apple: '🍎', lemon: '🍋' } ✅
obj2 // { mango: '🥭️', apple: '🍎', lemon: '🍋' } ❌
);
您可以看到我只對 obj1
進行了更改 但它影響了obj2
也是。這不是我們在復制對象時所期望的。這是因為對像是引用類型,當我們使用 =
, 它只有 將指針複製到分配給對象的內存而不是實際值。
淺克隆與深克隆
淺克隆 只複製對像中可用的原始類型,如字符串、數字和布爾值。任何嵌套對像或數組都不會被遞歸複製。相反,只有對該對象的引用被複製到新對象。這意味著原始對象和復制對像都繼續引用同一個嵌套對象。
如果原始對象引用其他外部對象,則在創建對象的淺表副本時也不會遞歸複製它們。只複製對外部對象的引用。
另一方面,深度克隆 遞歸複製所有內容:原始數據類型、嵌套和外部對象、數組、函數、日期等。克隆的對象完全獨立於原始對象。
JavaScript 提供了許多方法來創建對象的淺層和深層克隆。您可以使用擴展運算符 (...
) 和 Object.assign()
快速創建淺對象副本的方法。對於對象的深度克隆,您可以編寫自己的自定義函數,也可以使用 Lodash 等第三方庫。
Object.assign()
方法
創建對象淺拷貝的最簡單快捷的方法是使用 ES6 的 Object.assign(target, source1, soure2, ...)
方法。該方法將一個或多個源對象的所有可枚舉自身屬性複製到目標對象,並返回目標對象:
const fruits = { mango: '🥭️', apple: '🍎' };
const moreFruits = Object.assign({}, fruits);
console.log(moreFruits);
// { mango: '🥭️', apple: '🍎' }
注意空的 {}
源對像作為第一個參數。這是確保原始對像不被更改所必需的。這種方式對IE等老瀏覽器缺乏支持,只適用於現代瀏覽器。
查看本指南以了解有關 Object.assign()
的更多信息 方法。
擴展運算符
擴展運算符 (...
) 是另一個 ES6 特性,它提供了一種簡單的方法來執行對象的淺層克隆,相當於 Object.assign()
確實:
const fruits = { mango: '🥭️', apple: '🍎' };
const moreFruits = { ...fruits };
console.log(moreFruits);
// { mango: '🥭️', apple: '🍎' }
儘管傳播運算符自 ES6(ESMAScript 2015)以來就存在,但對克隆對象的支持直到最近才在 ES9(ESMAScript 2018)中引入。所以你應該只考慮將這種方法用於最新版本的現代瀏覽器。
JSON 方法
如果您的對象僅包含原始類型,並且不包含嵌套或外部對象、數組、Date
對象、函數等,您可以使用 JSON 方法輕鬆創建對象的深層克隆:JSON.stringify()
和 JSON.parse()
:
const fruits = { mango: '🥭️', apple: '🍎' };
const moreFruits = JSON.parse(JSON.stringify(fruits));
console.log(moreFruits);
// { mango: '🥭️', apple: '🍎' }
這種方法適用於所有現代瀏覽器和 IE8+。但是,有兩個缺點:
- 對象必須與 JSON 格式兼容。這意味著嵌套對象必須是 JSON 可序列化和可反序列化的。
- 當對象包含大量屬性時,它比其他解決方案慢。
JSON 方法僅支持沒有函數和符號屬性的字符串、數字和對象文字。當對象包含不兼容的值時,您會看到一種奇怪的行為:
// undefined is omitted
// Infinity is turned to null
JSON.parse(JSON.stringify({ a: undefined, b: Infinity }));
// { b: null }
// Date object is turned to string
JSON.parse(JSON.stringify({ a: new Date() }));
// { a: "2020-06-16T19:44:57.492Z" }
// function is omitted too
JSON.parse(JSON.stringify({ a: () => { return 'Hi'; } }));
// {}
你應該只 將此方法用於 JSON 兼容對象。對於包含 JSON 不兼容值的對象,請考慮使用 Lodash 之類的第 3 方庫來創建深度克隆。
Lodash 的 cloneDeep()
方法
Lodash 提供了 cloneDeep()
遞歸地將原始對像中的所有內容複製到新對象的方法。它適用於所有數據類型,包括函數、嵌套對象、數組和符號。
這是一個例子:
const _ = require('lodash');
const obj = {
name: 'John Doe',
age: 45,
address: {
city: 'Berlin',
country: 'DE'
},
job: undefined,
credits: Infinity
};
const cloned = _.cloneDeep(obj);
console.log(cloned);
// {
// name: 'John Doe',
// age: 45,
// address: { city: 'Berlin', country: 'DE' },
// job: undefined
// credits: Infinity
// }
要了解有關 JavaScript 對象、原型和類的更多信息,請查看這篇文章。
閱讀下一篇: 如何在 JavaScript 中復制數組