參考:Copying Objects in JavaScript - Orinami Olatunji(@orinamio_) October 23, 2017
直接將一個變量賦給另一個變量時,系統並不會創造一個新的變量,而是將原變量的地址賦給了新變量名。舉個栗子:
let obj = { a: 1, b: 2, }; let copy = obj; obj.a = 5; console.log(copy.a); // Result // a = 5; // 更改obj的值,copy變量的值也會改變
文章中提到了很多種辦法,本文只選擇了三種普遍的用法並分析了各自的優缺點,以及什么情況下使用哪種是最好的。
1. 原生方法解決
最簡單的辦法就是一個一個循環復制給新的變量。舉栗:
function copy(mainObj) { let objCopy = {}; // objCopy will store a copy of the mainObj let key; for (key in mainObj) { objCopy[key] = mainObj[key]; // copies each property to the objCopy object } return objCopy; } const mainObj = { a: 2, b: 5, c: { x: 7, y: 4, }, } console.log(copy(mainObj));
缺點:
1. objCopy 的Object.prototype 方法與mainObj 會不一樣,通常情況下我們需要完全一樣的副本時,這個辦法並不適用。
2. 麻煩而且費時費事,代碼無法重用。
3. 如果原來的變量中包含Object類型,復制時還是會把這個子變量的索引交給新的變量,並不是創建了新的副本。
2. 深度復制
利用JSON轉換來復制變量。先將原先的變量轉換為String然后再重新組裝成JSON,這樣會產生一個不一樣的副本。
let obj = { a: 1, b: { c: 2, }, } let newObj = JSON.parse(JSON.stringify(obj)); obj.b.c = 20; console.log(obj); // { a: 1, b: { c: 20 } } console.log(newObj); // { a: 1, b: { c: 2 } } (New Object Intact!)
缺點:
1. 變量很多的時候非常耗時耗內存。
3. 使用Object.assign()
使用舉例:
// circular object let obj = { a: 'a', b: { c: 'c', d: 'd', }, } obj.c = obj.b; obj.e = obj.a; obj.b.c = obj.c; obj.b.d = obj.b; obj.b.e = obj.b.c; let newObj2 = Object.assign({}, obj); console.log(newObj2);
可以把它封裝成一個方法:
// 封裝成方法
// 返回一個新的變量副本 // get a copy of an object function getNewObjectOf(src) { return Object.assign({}, src); }
缺點:
1. 這個也是淺復制(僅復制頂層的屬性,底層屬性並不復制)。深層屬性會同樣返回索引,與原變量分享一個地址。(看下面栗子)
let obj = { a: 1, b: { c: 2, }, } let newObj = Object.assign({}, obj); console.log(newObj); // { a: 1, b: { c: 2} } obj.a = 10; console.log(obj); // { a: 10, b: { c: 2} } console.log(newObj); // { a: 1, b: { c: 2} } newObj.a = 20; console.log(obj); // { a: 10, b: { c: 2} } console.log(newObj); // { a: 20, b: { c: 2} } newObj.b.c = 30; console.log(obj); // { a: 10, b: { c: 30} } console.log(newObj); // { a: 20, b: { c: 30} } // 注意: 所有變量 的 *。b.c 都等於30; 原因看上面解釋。
結論:
原文中還有很多其他的辦法,但此文僅摘抄出最有用的幾個。一般不會用到第一種辦法,如需要復制的變量有很多層的話,需要用第二種辦法來復制,如果變量僅僅包含一層(如json格式的配置信息變量),第三種是最高效的。
再次給出封裝好的方法:
// 封裝成方法
// 返回一個新的變量副本 // get a copy of an object function getNewObjectOf(src) { return Object.assign({}, src); }