js deep clone 深克隆

original_galaxy發表於2019-02-19

首先,值的拷貝,通常有三種方式,由於基本型別與引用型別在記憶體中儲存位置和儲存方式的不同,導致了以下三種概念的衍生:

  • = 賦值:多個指標指向的是同一個堆中的地址,所以相互有影響;
  • 淺拷貝:在堆中重新建立記憶體,拷貝前後基本資料型別不受影響,但只拷貝一層,無法拷貝子物件;所以改變淺拷貝得到的物件中的引用型別時,原始資料會受到影響;例如陣列的concat和slice方法;
  • 深拷貝:對子物件也可以拷貝,拷貝前後兩個物件互不影響,是對物件以及物件的所有子物件進行拷貝;思路就是遞迴呼叫淺拷貝的邏輯,把所有屬於物件的屬性型別都遍歷賦給另一個物件即可。完全的拷貝一個物件,即使巢狀了物件,兩者也相互分離,修改一個物件的屬性,也不會影響另一個。


看下陣列結構的拷貝:

var new_arr = JSON.parse( JSON.stringify(arr) );複製程式碼

但此法無法拷貝函式。concat、slice、JSON.stringify 都算是技巧類,可以根據實際情況適當使用。

初步實現一個淺拷貝:

在看開源專案的過程中,經常會看到類似如下的原始碼。for...in迴圈物件的所有列舉屬性,然後再使用hasOwnProperty()方法來忽略繼承屬性。

const shallowClone = (obj) => {  
    if (typeof obj !== 'object') return
    let newObj = obj instanceof Array ? [] : {}  
    for (let key in obj) {    
        if (obj.hasOwnProperty(key)) {      
            newObj[key] = obj[key]    
        }  
     }
     return newObj
}複製程式碼

初步實現一個深拷貝:

const deepClone = (obj) => {  
    if (typeof obj !== 'object') return
    let newObj = obj instanceof Array ? [] : {}  
    for (let key in obj) {    
       if (typeof obj[key] === 'object') {      
         newObj[key] = deepClone(obj[key])    
       } else {      
          newObj[key] = obj[key]    
       }  
     }
     return newObj
}複製程式碼

深拷貝會完全的克隆一個新物件,但因為使用遞迴,效能會不如淺拷貝,在開發中,還是要根據實際情況進行選擇。


第三方庫的實現

Underscore _.clone()

實際上是一種淺複製 (shallow-copy),所有巢狀的物件和陣列都是直接複製引用而並沒有進行深複製。原始碼:

// 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()

var x = {
    a: 1,
    b: { f: { g: 1 } },
    c: [ 1, 2, 3 ]
};

var y = $.extend({}, x),          //shallow copy
    z = $.extend(true, {}, x);    //deep copy

y.b.f === x.b.f       // true
z.b.f === x.b.f       // false複製程式碼

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

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

jQuery 無法正確深複製 JSON 物件以外的物件,而 lodash 花了大量的程式碼來實現 ES6 引入的大量新的標準物件。lodash 針對存在環的物件的處理也是非常出色的。因此相較而言,lodash 在深複製上的行為反饋比前兩個庫好很多。


參考源:

https://juejin.im/post/59ac1c4ef265da248e75892b

https://segmentfault.com/a/1190000002801042

https://github.com/mqyqingfeng/Blog/issues/32



相關文章