複習Javascript專題(四):js中的深淺拷貝

基本數據類型的複製很簡單,就是賦值操做,因此深淺拷貝也是針對Object,Array這類引用類型數據。javascript

淺拷貝對於字符串來講,是值的複製,而對於對象來講則是對對象地址的複製;

而深拷貝的話,它不只將對象的各個屬性逐個複製出來,還將各個屬性所包含的對象也依次複製出來,至關於在堆區又新開了一塊地存放,主要採起遞歸來實現。css

實現方式

原生JS實現:

淺拷貝:java

let originObj={ color:['red','green'], num:5 };

function shallowClone(obj){
    let newObj=(obj instanceof Array) ? []:{};
    for(let item in obj){
        if(obj.hasOwnProperty(item)){ // 避免列舉出原型上的屬性
            newObj[item]=obj[item];
        }
    }
    return newObj;
}
let cloneObj=shallowClone(originObj);

深拷貝:算法

function deepClone(obj){
       let newObj=(obj instanceof Array)? [] : {};
       for(let item in obj){
           if(obj.hasOwnProperty){
               const val=obj[item];
               debugger
               newObj[item]=typeof val==='object' ? deepClone(val) : val;
           }
       }
       return newObj;
   }
   let cloneObj2=deepClone(originObj);

其實,這種深拷貝的實現仍是有些問題的,好比日期正則對象沒法複製,還有一個是循環引用,相似閉包會內存泄露同樣,這樣的對象深拷貝也會陷入一個閉環,直到棧溢出。
爲了解決這個問題,就得先判斷這個對象是否等於原對象。segmentfault

function deepClone(obj){
    let tempArr=[];
    let newObj=(obj instanceof Array) ? [] : {};
    tempArr.push(obj);
    for(let item in obj){
        if(typeof obj[item] === 'object'){
            const index=tempArr.indexOf(obj[item]); 
            // 若是已存在,證實引用了相同對象,那麼不管是循環引用仍是重複引用,咱們返回引用就能夠了
            if(index>-1){
                newObj[item]=tempArr[index];
            }else{
                newObj[item]=obj[item];
            }
        }else{
            newObj[item]=obj[item];
        }        
    }
    return newObj;
}

ES6實現

let newObj1=Object.assgin({},originObj);
    let newObj2={...obj}; // 數組用[...obj]
注:Object.assgin和擴展運算符實現的都是淺拷貝。

JSON實現:

這種方法簡單是簡單,不過這個方法有如下缺陷:數組

1.會忽略函數對象以及原型對象,正則會複製成空對象,而日期對象會變成字符串。

2.它會拋棄對象的constructor。也就是深拷貝以後,無論這個對象原來的構造函數是什麼,在深拷貝以後都會變成Object; 
   
3.若是對象中存在循環引用的狀況沒法正確處理。

按需取用吧。閉包

let originObj={fn:function(){console.log(111)}, colors:['red','gree'],reg:/\s/g};
  let newObj=JSON.parse(JSON.stringify(originObj));

第三方庫實現:

轉自連接:深刻剖析 JavaScript 的深複製ide

Underscore —— _.clone()

順便安利下underscore,真的挺好用的一個庫: Underscore中文文檔

這個方法其實是一種淺複製 (shallow-copy),全部嵌套的對象和數組都是直接複製引用而並無進行深複製。函數

let originObj={ color:['red','green'], num:5 };
        let newObj=_.clone(originObj);
        newObj.colors.push('grey');
        originObj.colors; // ['red','green','grey'];

源碼邏輯很簡單:(固然調用的其餘方法也是underscore的)post

// Create a (shallow-cloned) duplicate of an object.
    _.clone = function(obj) {
        if (!_.isObject(obj)) return obj;
        return _.isArray(obj) ? obj.slice() : _.extend({}, obj);
    }

jQuery —— $.extend()

這個用法很簡單:

let originObj={ color:['red','green'], num:5 };
    let shallowClone=$.extend({},originObj); // 淺拷貝
    let deepClone=$.extend(true,{},originObj); // 深拷貝,第一個參數表示是否深度合併對象

lodash —— _.clone() / _.cloneDeep()

lodash深拷貝——這個算是這幾個裏面最完善的方法了, 日期函數正則對象統統都能複製。

在lodash中關於複製的方法有兩個,分別是_.clone()_.cloneDeep()。其中_.clone(obj, true)等價於_.cloneDeep(obj)

沒引入的能夠點擊上面的文檔連接去直接改動代碼試試看。

另外,查資料過程當中還看到這麼一個詞: 結構化克隆算法
還有這一篇資料也有參考,也寫得比較詳細了: JS的深淺拷貝
相關文章
相關標籤/搜索