JavaScript的深浅复制

2022-10-13,,

javascript深浅复制

为什么有深复制、浅复制?

javascript中有两种数据类型,基本数据类型如undefinednullbooleannumberstring,另一类是object。简单数据类型只存储在内存中的栈区,复制的时候是值传递给新的索引。而复杂数据类型由栈区和堆区共同储存,栈区执行同样的操作,只是把堆地址复制了一份,而真实数据在堆区中依然只有一份。
为了不影响原有数据,那么我们就新建一个对象,遍历原有对象的属性赋值到新属性。

let newobj = {}
for (let prop in obj) {
  newobj[prop] = obj[prop]
}

上面这个循环也可以用object.assign({}, obj);来实现。
这样做是否解决问题?未必,因为object中可以嵌套object,如果原有对象属性中有复杂数据类型,那么新的对象中也只能得到一个地址。这种情况被称为浅复制。我们希望能将对象中的对象,无论多少层,都能复制一份,能达到这种效果的,称为深复制

深复制的几种方法

首先假设有数据

let obj = {
    a: 23,
    b: [0, 1, [2, 3], function() {console.log('in array')}, undefined],
    c: {k: 'value'},
    d: function() {console.log('a')}
    }

json.parse(json.stringify(obj))

let newobj = json.parse(json.stringify(obj))
newobj.newkey = 'newvalue'
console.log(obj)
console.log(newobj)

如果处理对象只是简单的键值对,这个方法效果不错。

这个方法的缺点

  • 无法复制函数
  • 忽略undefined
  • 无法处理set、map、symbol类型(即使用上repalce参数)
  • 原有的原型链会消失
  • 循环引用的对象会报错

    递归法

    因为要处理属性的值也是object这种情况,自然可以想到递归这种处理方法。

function deepcopy(oldobj, newobj) {
    let obj = newobj || {} 
    for (let i in oldobj) {
        if (oldobj[i] === obj) { // 防止循环引用
            continue
        }

        if (typeof oldobj[i] === 'object') {
            // obj[i] = (oldobj[i].constructor === array) ? [] : {}
            obj[i] = oldobj[i] instanceof array ? [] : {}
            deepcopy(oldobj[i], obj[i])
        } else {
            obj[i] = oldobj[i]
        }
    }
    return obj;
}

这样就能处理一个嵌套了object和array等复杂变量的对象。但是对象中还可能包含date和regexp对象、set、map……

lodash库

const lodash = require('lodash')
let newobj = lodash.clonedeep(obj)

数组的复制

let arr = [1, 2, 3, [4, 5], {a: 1}]

let copy = arr
copy.push(6)

let copy1 = [...arr]
copy1.push(999)

let copy2 = array.from(arr)
copy2.push(888)

let copy3 = arr.slice()
copy3.push(777)

// 以上方法都是浅拷贝
arr[4].a = 2

console.log(arr)   // [ 1, 2, 3, [ 4, 5 ], { a: 2 }, 6 ]
console.log(copy1) // [ 1, 2, 3, [ 4, 5 ], { a: 2 }, 6 ]
console.log(copy2) // [ 1, 2, 3, [ 4, 5 ], { a: 2 }, 6, 888 ]
console.log(copy3) // [ 1, 2, 3, [ 4, 5 ], { a: 2 }, 6, 777 ]

参考连接

  1. 摸索 js 内深拷贝的最佳实践 - 简单易懂的前端角 - segmentfault 思否
  2. 理解javascript:不可变的原始值与可变的对象引用
  3. [ js 进阶 ] 基本类型 引用类型 简单赋值 对象引用 - kraaas前端博客 - segmentfault 思否
  4. 深拷贝的终极探索(99%的人都不知道) - 颜海镜 - segmentfault 思否

《JavaScript的深浅复制.doc》

下载本文的Word格式文档,以方便收藏与打印。