不要眼高手低,手动实现深拷贝和浅拷贝

261 阅读2分钟

概念

浅拷贝: 浅拷贝指的是将一个对象的属性值复制到另一个对象,如果属性值是引用类型的话,那么会将这个引用的地址复制给新对象。因此两个对象会有同一个引用类型的引用。浅拷贝可以用Object.assign()和扩展运算符来实现。

深拷贝: 深拷贝相对于浅拷贝而言,如果遇到属性值为引用类型的时候,它新建一个引用类型并将对应的值赋给它,因此对象获得一个新的引用类型而不是一个原有类型的引用。

实现

浅拷贝

浅拷贝一般有以下几种实现方法:

  • Object.assign()
  • 扩展运算符
  • 手写实现

Object.assign

Object.assign是ES6中对象的拷贝方法,其用法详见MDN

const obj = {
    name: 'zhangsan',
    b: {
        bb: 123
    }
}

const newObj = Object.assign({}, obj)

// 引用类型修改之后,newObj也会修改
obj.b.bb = 234
// newObj不会变
obj.name = 'lisi'
console.log(obj, newObj);

image.png

注意,如果原数据(obj)只有一层(基本数据类型)时,就是深拷贝。所以当修改obj的name属性时,newObj中的name不会变。

扩展运算符

ES2018把扩展运算符...引入到了对象。

const obj = {
    name: 'zhangsan',
    b: {
        bb: 123
    }
}

const newObj = {...obj}

// 引用类型修改之后,newObj也会修改
obj.b.bb = 234
// newObj不会变
obj.name = 'lisi'
console.log(obj, newObj);

image.png

注意,同上Object.assign,当只有一层数据时是深拷贝。

自己实现浅拷贝

const obj = {
    name: 'zhangsan',
    b: {
        bb: 123
    }
}

function shallowCopy(o) {

    if (!o || typeof o !== "object") {
        return
    }

    let res = Array.isArray(o) ? [] : {}

    for (let key in o) {
        if (o.hasOwnProperty(key)) {
            res[key] = o[key]
        }
    }
    return res
}

const newObj = shallowCopy(obj)

obj.name = 'lisi'
obj.b.bb = '234'

console.log(obj, newObj);

image.png

深拷贝

深拷贝一般有以下几种实现方法:

  • JSON.parse(JSON.stringify(obj))
  • lodash的_.cloneDeep
  • 手写实现深拷贝

JSON.parse(JSON.stringify(obj))

常用方法之一,原理是先用JSON.stringify()把js对象序列化成json字符串,再用JSON.parse()来反序列化json字符串成js对象。这个方法简单粗暴,但是当对象中有函数、undefined、symbol时,JSON.stringify后都会消失。

const obj = {
    name: 'zhangsan',
    b: {
        bb: 123
    }
}

const newObj = JSON.parse(JSON.stringify(obj))

obj.name = 'lisi'
obj.b.bb = 234

console.log(obj, newObj);

image.png

lodash的_cloneDeep

详见lodash cloneDeep

手动实现深拷贝

const obj = {
    name: 'zhangsan',
    b: {
        bb: 123
    }
}

function cloneDeep(o) {
    if (!o || typeof o !== "object") {
        return
    }

    const res = Array.isArray(o) ? [] : {}

    for (let key in o) {
        if (o.hasOwnProperty(key)) {
            if (typeof o[key] === "object") {
                res[key] = cloneDeep(o[key])
            } else {
                res[key] = o[key]
            }
        }
    }
    return res
}

const newObj = cloneDeep(obj)

obj.name = 'lisi'
obj.b.bb = 234

console.log(obj, newObj);

image.png