JS深拷貝總結

JS的原生不支持深拷貝, Object.assign{...obj}都屬於淺拷貝,下面咱們講解如何使用JS實現深拷貝。

JSON.sringify 和 JSON.parse 

這是JS實現深拷貝最簡單的方法了,原理就是先將對象轉換爲字符串,再經過JSON.parse從新創建一個對象。 可是這種方法的侷限也不少:javascript

  • 不能複製function、正則、Symbol
  • 循環引用報錯
  • 相同的引用會被重複複製

咱們依次看看這三點,咱們測試一下這段代碼:java

let obj = {         
    reg : /^asd$/,
    fun: function(){},
    syb:Symbol('foo'),
    asd:'asd'
}; 
let cp = JSON.parse(JSON.stringify(obj));
console.log(cp);複製代碼

結果:數組


能夠看到,函數、正則、Symbol都沒有被正確的複製。函數

若是在JSON.stringify中傳入一個循環引用的對象,那麼會直接報錯:測試


在說第三點以前,咱們看看這段代碼:ui

let obj = {  asd:'asd' }; 
let obj2 = {name:'aaaaa'};
obj.ttt1 = obj2;
obj.ttt2 = obj2;
let cp = JSON.parse(JSON.stringify(obj)); 
obj.ttt1.name = 'change'; 
cp.ttt1.name  = 'change';
console.log(obj,cp);複製代碼

在原對象 obj 中的 ttt1ttt2 指向了同一個對象 obj2,那麼我在深拷貝的時候,就應該只拷貝一次 obj2 ,下面咱們看看運行結果:spa


咱們能夠看到(上面的爲原對象,下面的爲複製對象),原對象改變 ttt1.name 也會改變 ttt2.name ,由於他們指向相同的對象。code

可是,複製的對象中,ttt1 和 ttt2 分別指向了兩個對象。複製對象沒有保持和原對象同樣的結構。所以,JSON實現深複製不能處理指向相同引用的狀況,相同的引用會被重複複製。regexp

遞歸實現

JS原生的方法不能很好的實現深複製,那麼咱們就動手實現一個。cdn

思想很是簡單:對於簡單類型,直接複製。對於引用類型,遞歸複製它的每個屬性。

咱們須要解決的問題:

  • 循環引用
  • 相同引用
  • 不一樣的類型(筆者僅實現了數組和對象的區分)

實現代碼:

function deepCopy(target){ 
let copyed_objs = [];//此數組解決了循環引用和相同引用的問題,它存放已經遞歸到的目標對象 
    function _deepCopy(target){ 
        if((typeof target !== 'object')||!target){return target;}
        for(let i= 0 ;i<copyed_objs.length;i++){
            if(copyed_objs[i].target === target){
                return copyed_objs[i].copyTarget;
            }
        }
        let obj = {};
        if(Array.isArray(target)){
            obj = [];//處理target是數組的狀況 
        }
        copyed_objs.push({target:target,copyTarget:obj}) 
        Object.keys(target).forEach(key=>{ 
            if(obj[key]){ return;} 
            obj[key] = _deepCopy(target[key]);
        }); 
        return obj;
    } 
    return _deepCopy(target);
}
複製代碼

copyed_objs 這個數組存放的是已經遞歸過的目標對象。在遞歸一個目標對象以前,咱們應該檢查這個數組,若是當前目標對象和 copyed_objs 中的某個對象相等,那麼不對其遞歸。

這樣就解決了循環引用和相同引用的問題。

測試一下代碼:

var a = {
    arr:[1,2,3,{key:'123'}],//數組測試
};
a.self = a;//循環引用測試
a.common1 = {name:'ccc'};
a.common2 = a.common1;//相同引用測試
var c = deepCopy(a);
c.common1.name = 'changed';
console.log(c);複製代碼

結果:


能夠看到,前文提到的問題都已經解決。

最後補充:

本文實現的深拷貝僅僅是解決了深複製的關鍵問題,還須要針對不一樣的數據類型進行完善。

相關文章
相關標籤/搜索