在本文中,我們將從淺拷貝(shallow copy)和深拷貝(deep copy)兩個方面,介紹多種 js 中複製物件的方法。
var foo =
console.log(foo.a); // abc
var bar = foo;
console.log(bar.a); // abc
foo.a = "yo foo";
console.log(foo.a); // yo foo
console.log(bar.a); // yo foo
bar.a = "whatup bar?";
console.log(foo.a); // whatup bar?
console.log(bar.a); // whatup bar?
js
如上所示,對 foo 和 bar 兩個物件中的任乙個做修改,另乙個都會發生相應變化。因此,在 js 中複製物件要格外小心。
淺拷貝如果物件比較簡單、只具有值型別的屬性,可以使用擴充套件運算子(spread)或object.assign(...)
var obj = ;
var copy = ; // object
js
var obj = ;
var copy = object.assign({}, obj); // object
js
注:上述兩種方法都可以將屬性值從多個源物件複製到目標物件:
var obj1 = ;
var obj2 = ;
var copyspread = ; // object
var copyassign = object.assign({}, obj1, obj2); // object
js
不過,如果物件的屬性值也是乙個物件,那麼用上述方法拷貝就會有問題了:那樣做只是建立了乙個物件屬性值引用的副本(但共享的還是乙個記憶體),和本文開篇第一段**示例中var bar = foo;
的效果一樣:
var foo = };
var copy = ;
copy.a = 1;
copy.b.c = 2;
// 修改副本 copy 中的屬性 c,原物件 foo 中的屬性也跟著變化
console.dir(foo); // }
console.dir(copy); // }
js
深拷貝(有注意事項)
深拷貝物件,一種解決方案是將物件序列化為字串,然後再將其反序列化:
var obj = };
var copy = json.parse(json.stringify(obj));
js
然而,此方法僅在原物件包含可序列化值型別且沒有任何迴圈引用時才有效。不可序列化值型別的乙個例子是date
物件 -json.parse
只能將其解析為字串而無法解析回其原始的date
物件 :(。
複雜物件的深拷貝
對於更複雜的物件,可以使用新的由 html5 規範定義的 "結構化轉殖(structured clone)" 演算法。不過,雖然這種方法支援的內容型別多於json.parse
,但在實際運用中,仍侷限於某些內建型別,如:date,regexp,map,set,blob,filelist,imagedata,稀疏和型別化陣列(typed array)。它還保留了轉殖資料中的引用,支援迴圈和遞迴結構。
目前,沒有直接的方法來呼叫結構化轉殖演算法,但是有一些較新的瀏覽器功能使用這種演算法。因此,有一些可用的深拷貝物件的方法。
通過 messagechannels:利用通訊功能使用的序列化演算法。由於此功能是基於事件的,因此生成的轉殖也是非同步操作。
class structuredcloner }) => ;
this.outport_.start();
} cloneasync(value) );
});}}
const structuredcloneasync = window.structuredcloneasync =
structuredcloner.prototype.cloneasync.bind(new structuredcloner);
const main = async () => ;
original.self = original;
const clone = await structuredcloneasync(original);
// different objects:
console.assert(original !== clone);
console.assert(original.date !== clone.date);
// cyclical:
console.assert(original.self === original);
console.assert(clone.self === clone);
// equivalent values:
console.assert(original.number === clone.number);
console.assert(number(original.date) === number(clone.date));
console.log("assertions complete.");
};main();
js
通過 history api:history.pushstate()
和history.replacestate()
建立裡面第乙個引數obj
的結構化轉殖!注意,雖然此方法是同步的,但操縱瀏覽器的歷史記錄並不是一項快速的操作,也需要一定的過程和時間,反覆呼叫此方法可能會導致瀏覽器無響應。
const structuredclone = obj => ;
js
通過 notification api:在建立新通知時,建構函式會建立其關聯資料的結構化轉殖。請注意,它還會嘗試向使用者顯示瀏覽器通知,但除非應用程式已申請顯示通知的許可權,否則將以靜默方式失敗。在授予許可權的情況下,通知會立即關閉。
const structuredclone = obj => );
n.onshow = n.close.bind(n);
return n.data;
};
js
node.js 中的深拷貝
從 8.0.0 版開始,node.js 提供了與結構化轉殖相容的 serialization api。請注意,在撰寫本文時,此 api 已標記為實驗性:
const v8 = require('v8');
const buf = v8.serialize();
const cloned = v8.deserialize(buf);
cloned.b.getmonth();
js
對於低於 8.0.0 的版本或為了更穩定地實現轉殖,可以使用 lodash 的clonedeep
方法,該方法也基於結構化轉殖演算法。
總結總而言之,在 js 中用哪種方式複製物件,在很大程度上取決於你要複製的物件的上下文和型別。雖然 lodash 是通用深拷貝最安全的選擇,但如果自己動手寫,可能會有更具針對性、更高效的實現辦法,以下是乙個適用於深度轉殖日期的簡單示例:
function deepclone(obj)
// handle array
if (obj instanceof array)
return copy;
} // handle function
if (obj instanceof function)
return copy;
} // handle object
if (obj instanceof object) ;
for (var attr in obj)
return copy;
} throw new error("unable to copy obj as type isn't supported " + obj.constructor.name);
}
js
就個人而言,我期待能夠在任何地方使用結構化轉殖。
複製物件 和 淺拷貝,深拷貝
複製物件顧名思義,複製乙個物件作為副本,它會開闢一塊新的記憶體 堆記憶體 來儲存副本物件,就像複製檔案一樣.既源物件和副本物件是兩塊不同的記憶體區域.物件具備複製功能,必須實現 協議協議 常用的可複製物件有,nsnumber,nsstring,nsarray,nsdictionary,nsmutab...
js 物件深拷貝 深拷貝與淺拷貝
前言 最近在複習一些面試的知識點,剛剛好複習到了這一部分,於是就寫下這篇文章記錄一下。一 值型別和引用型別 在學習深拷貝和淺拷貝之前,我們先來了解一下js的變數型別。值型別 vs 引用型別 值型別 值型別主要有 number,string,boolean,symbol,null,undefined ...
js物件淺拷貝和深拷貝
1 淺拷貝 varobj 定義乙個物件 functioncopy obj 定義乙個空物件,用來儲存key和value for varkeyinobj returnnewobj 將新物件作為返回值,返回到外面 varobj2 copy obj 將obj複製給obj2 obj2.a 20 改變obj2中...