咱們在JS程序中須要進行頻繁的變量賦值運算,對於字符串、布爾值等可直接使用賦值運算符 「=」 便可,可是對於數組、對象、對象數組的拷貝,咱們須要理解更多的內容。數組
首先,咱們須要瞭解JS的淺拷貝與深拷貝的區別。函數
咱們先給出一個數組:測試
var arr = ["a","b"];
如今怎麼建立一份arr數組的拷貝呢?直接執行賦值運算嗎?咱們來看看輸出結果spa
var arrCopy = arr; arrCopy[1] = "c"; arr // => ["a","c"]
能夠發現對拷貝數組 arrCopy 進行操做時原數組也相應地被改變了,這就是JS的淺拷貝模式。因此咱們能夠指出對數組、對象、對象數組進行簡單賦值運算只是建立了一份原內容的引用,指向的仍然是同一塊內存區域,修改時會對應修改原內容,而有時候咱們並不須要這種模式,這就須要對內容進行深拷貝。code
對於數組的深拷貝常規的有三種方法:對象
var arr = ["a", "b"], arrCopy = []; for (var item in arr) arrCopy[item] = arr[item]; arrCopy[1] = "c"; arr // => ["a", "b"] arrCopy // => ["a", "c"]
考慮僞多維數組能夠寫成函數形式:blog
function arrDeepCopy(source){ var sourceCopy = []; for (var item in source) sourceCopy[item] = typeof source[item] === 'object' ? arrDeepCopy(source[item]) : source[item]; return sourceCopy; }
這種方法簡單粗暴,可是利用JS自己的函數咱們能夠更加便捷地實現這個操做。內存
能夠參考 W3School 對 slice() 方法的描述:slice() 方法可從已有的數組中返回選定的元素。字符串
調用格式爲:it
arrayObject.slice(start,end)
方法返回一個新的數組,包含從 start 到 end (不包括該元素)的 arrayObject 中的元素。該方法並不會修改數組,而是返回一個子數組。
在這裏咱們的思路是直接從數組開頭截到尾:
arrCopy = arr.slice(0); arrCopy[1] = "c"; arr // => ["a", "b"] arrCopy // => ["a", "c"]
能夠看出成功建立了一份原數組的拷貝。
能夠參考 W3School 對 concat() 方法的描述:concat() 方法用於鏈接兩個或多個數組。
調用格式爲:
arrayObject.concat(arrayX,arrayX,......,arrayX)
該方法不會改變現有的數組,而僅僅會返回被鏈接數組的一個副本。
使用這種方法的思路是咱們用原數組去拼接一個空內容,放回的即是這個數組的拷貝:
arrCopy = arr.concat(); arrCopy[1] = "c"; arr // => ["a", "b"] arrCopy // => ["a", "c"]
對於數組的深拷貝咱們有了概念,那麼通常對象呢?
咱們給出一個對象:
var obj = { "a": 1, "b": 2 };
一樣作測試:
var objCopy = obj; objCopy.b = 3; obj // => { "a": 1, "b": 3 } objCopy // => { "a": 1, "b": 3
一樣,簡單的賦值運算只是建立了一份淺拷貝。
而對於對象的深拷貝,沒有內置方法可使用,咱們能夠本身命名一個函數進行這一操做:
var objDeepCopy = function(source){ var sourceCopy = {}; for (var item in source) sourceCopy[item] = source[item]; return sourceCopy; }
可是對於複雜結構的對象咱們發現這個函數並不適用,例如:
var obj = { "a": { "a1": ["a11", "a12"], "a2": 1 }, "b": 2 };
因此須要進行一點修改:
var objDeepCopy = function(source){ var sourceCopy = {}; for (var item in source) sourceCopy[item] = typeof source[item] === 'object' ? objDeepCopy(source[item]) : source[item]; return sourceCopy; } var objCopy = objDeepCopy(obj); objCopy.a.a1[1] = "a13"; obj // => { "a": { "a1": ["a11", "a12"], "a2": 1 }, "b": 2 } objCopy // => { "a": { "a1": ["a11", "a13"], "a2": 1 }, "b": 2 }
若是再考慮更奇葩更復雜的狀況,例如咱們定義:
var obj = [{ "a": { "a1": ["a11", "a12"], "a2": 1 }, "b": 2 }, ["c", { "d": 4, "e": 5 }]];
這是一個由對象、數組雜合成的奇葩數組,雖然咱們平時寫程序基本不可能這麼折騰本身,可是能夠做爲一種特殊狀況來考慮,這樣咱們就能夠結合以前說的方法去拓展拷貝函數:
var objDeepCopy = function (source) { var sourceCopy = source instanceof Array ? [] : {}; for (var item in source) { sourceCopy[item] = typeof source[item] === 'object' ? objDeepCopy(source[item]) : source[item]; } return sourceCopy; } var objCopy = objDeepCopy(obj); objCopy[0].a.a1[1] = "a13"; objCopy[1][1].e = "6"; obj // => [{ "a": { "a1": ["a11", "a12"], "a2": 1 }, "b": 2 }, ["c", { "d": 4, "e": 5 }]] objCopy // => [{ "a": { "a1": ["a11", "a13"], "a2": 1 }, "b": 2 }, ["c", { "d": 4, "e": 6 }]]
這樣它就能夠做爲一個通用函數替咱們進行深拷貝操做了。