1. 程式人生 > 實用技巧 >js實現深拷貝(深度克隆)

js實現深拷貝(深度克隆)

淺克隆(淺拷貝)

在資料型別為引用型別的時候,當你給這個變數賦值,其實是引用這個變數在記憶體中的地址。如下:

var obj = {name: 'ccc', age: 18}    // 定義一個變數為物件,引用型別
var cloneObj = obj      // 建立一個新變數,並賦值
console.log(cloneObj)   // {name: 'ccc', age: 18}  
console.log(cloneObj === obj)   // true

淺克隆帶來的問題:

var obj = {name: 'ccc', age: 18}    // 定義一個變數為物件,引用型別
var cloneObj = obj      // 建立一個新變數,並賦值
console.log(cloneObj)   // {name: 'ccc', age: 18}  
console.log(cloneObj === obj)   // true

obj.name = 'www'
console.log(cloneObj)   // { name: 'www', age: 18 }

我們可以發現,我們修改了obj變數的屬性值的時候,cloneObj的屬性值也跟著發生了變化。原因是他們雖然是兩個變數,但是引用的變數是同一個變數。看下圖分析:

深度克隆(深拷貝)

深度克隆,就是解決淺度克隆帶來的問題的。直接上程式碼:

function deepClone(o) {
    // 判斷如果不是引用型別,直接返回資料即可
    if (typeof o === 'string' || typeof o === 'number' || typeof o === 'boolean' || typeof o === 'undefined') {
        return o
    } else if (Array.isArray(o)) { // 如果是陣列,則定義一個新陣列,完成複製後返回
        // 注意,這裡判斷陣列不能用typeof,因為typeof Array 返回的是object
        console.log(typeof [])  // --> object
        var _arr = []
        o.forEach(item => { _arr.push(item) })
        return _arr
    } else if (typeof o === 'object') {
        var _o = {}
        for (let key in o) {
            _o[key] = deepClone(o[key])
        }
        return _o
    }
}

var arr = [1, 2, 3, 5]
var cloneArr = deepClone(arr)
console.log(cloneArr)   // --> [ 1, 2, 3, 5 ]
console.log(arr === cloneArr)   // --> false

var obj = { name: 'ccc', age: 18 }
var cloneObj = deepClone(obj)
console.log(cloneObj)   // --> { name: 'ccc', age: 18 }
console.log(obj === cloneObj)   // false
obj.name = 'www'
console.log(obj)    // --> { name: 'www', age: 18 }
console.log(cloneObj)   // --> { name: 'ccc', age: 18 }

obj和cloneObj分別指向自己所存的變數地址,互不影響,程式碼註釋挺詳細了,看下圖:

注意:上圖深度克隆程式碼只供參考瞭解,還有很多細節沒有考慮,比如陣列和物件的巢狀拷貝等等,具體使用請檢視Lodash中的cloneDeep()方法。