JavaScript:利用遞迴實現物件深拷貝

XvJX發表於2019-02-16

先來普及一下深拷貝和淺拷貝的區別
淺拷貝:就是簡單的複製,用等號即可完成

let a = {a: 1}
let b = a

這就完成了一個淺拷貝
但是當修改物件b的時候,我們發現物件a的值也被改變了

b.a = 10
console.log(a.a) => 10

這是因為淺拷貝只複製了指向物件的指標,新舊物件共用同一塊記憶體,修改某一個物件的同時也會把另一個都一併修改了

深拷貝:跟淺拷貝最簡單明瞭的區別就是修改拷貝的物件,不會改變源物件
利用Object.assign可以對只有一層的物件實現深拷貝,如下:

let a = {a: 1,b: 2,c: 3}
let b = Object.assign({}, a)
b.b = 100
console.log(a.b) => 2

可以看出來這樣是完全可以做到對只有一層的物件實現深拷貝的
但是如果物件裡面的元素還是物件的話就沒作用了

let a = {a: 1,b: 2,c: 3, d: {a: 1}}
let b = Object.assign({}, a)
b.d.a = 100
console.log(a.d.a) => 100

對於這種比較複雜的物件,我們就可以利用遞迴的方式實現真正的物件深拷貝了

function deepClone (sourceObj, targetObj) {
    let cloneObj = targetObj || {}
    if(!sourceObj || typeof sourceObj !== "object" || sourceObj.length === undefined){
        return sourceObj
    }
    if(sourceObj instanceof Array){
        cloneObj = sourceObj.concat()
    } else {
        for(let i in sourceObj){
            if (typeof sourceObj[i] === `object`) {
                cloneObj[i] = deepClone(sourceObj[i], {})
            } else {
                cloneObj[i] = sourceObj[i]
            }
        }
    }
    return cloneObj
}

簡單的幾行程式碼就可以輕鬆實現物件的深拷貝

簡單的測試程式碼,如下:
let sourceObj = {
  a: 1,
  b: {
    a: 1
  },
  c: {
    a: 1,
    b: {
      a: 1
    }
  },
  d: function() {
    console.log(`hello world`)
  },
  e: [1, 2, 3]
}
let targetObj = deepClone(sourceObj, {})
targetObj.c.b.a = 9
console.log(sourceObj) => { a: 1,  b: { a: 1 },  c: { a: 1, b: { a: 1 } },  d: [Function: d],  e: [ 1, 2, 3 ] }
console.log(targetObj) => { a: 1,  b: { a: 1 },  c: { a: 1, b: { a: 9 } },  d: [Function: d],  e: [ 1, 2, 3 ] }

另外介紹兩個用來做深拷貝的庫

**jquery**
使用方法:
let targetObj = $.extent(true,{},sourceObj)
**lodash函式庫**
使用方法:
npm install lodash
**es5寫法**
let lodash = require(`lodash`)
**es6寫法**
import lodash from `lodash`

let targetOj = lodash.cloneDeep(sourceObj)

各位看官覺得有什麼地方不對的請多多指教。

相關文章