【JavaScript】对象的浅拷贝与深拷贝

前言

在 JavaScript 中,对象可谓是一个非常主要的知识点。什么原型链啊,拷贝啊,继承啊,创建啊等等等等。在我之前的文章中已经对对象的创建和继承做了一个简单的介绍,【JavaScript】ES5/ES6 创建对象与继承,那么这篇文章主要是针对对象的拷贝。

拷贝前的准备

我们先定义一个构造函数,创建好一个等待拷贝的对象。以下操作不考虑循环引用的问题。

function Person(name, age, job, ) {
    this.name = name
    this.age = age
    this.job = job
    this.height = function () { }
    this.weight = Symbol.for('weight')
    this.friend = {
        name: 'kangkan',
        age: 15
    }
}

Person.prototype.hobby = function () {
    return ['football', 'basketball']
}

const person = new Person('mike', null, undefined)
复制代码

浅拷贝

对象不同于 Number、String 等基础类型,它是一个引用类型,也就说它的值是保存在堆上,通过内存地址来访问的。简单来看

const a = {one: 1}
const b = {one: 1}
a === b // false
复制代码

如果 obejct1 的引用地址和 object2 一致,那么这就是浅拷贝,实现方式有三种。

直接赋值

const a = {one: 1}
const b = a
b === a // true
a.two = 2
console.log(b.two) // 2
复制代码

遍历拷贝

const simpleClone = function (target) {
    if (typeof target !== 'object') {
        throw new TypeError('arguments must be a Object!')
    }
    let obj = {}
    // 设置原型
    const prototype = Reflect.getPrototypeOf(target)
    Reflect.setPrototypeOf(obj, prototype)
    // 设置属性
    Reflect.ownKeys(target).forEach((key) => {
        obj[key] = target[key]
    })
    return obj
}
const clonePerson = simpleClone(person)
复制代码

可以看出拷贝的结果还是令人满意的。

下图 Object.assign(person) 应为 Object.assign({}, person)

遍历拷贝

Object.assign(target, source)

通过这个方法也能达到相同的效果

const simpleClonePerson = Object.assign({}, person)
复制代码

扩展运算符

const simpleClonePerson = {...person}
复制代码

扩展运算符

扫描二维码关注公众号,回复: 2463816 查看本文章

但是这里有个问题,原型对象丢失了。无法判断 simpleClonePerson 的实例。

但是操作一下 clonePerson.friend 对象,给它添加一个属性就会发现,person 对应的也增加了一个新属性。这不是我们的预期。

也就说通过 simpleClone 和 Object.assign 拷贝的对象只有第一层是深拷贝,第二层就是浅拷贝了。是对引用地址的拷贝。

深拷贝

简单来说,以上的浅拷贝方法,在对象深度只有一层的时候其实就是深拷贝。但是当对象的深度大于1,那么对象里面的对象就无法完成深拷贝了。

深拷贝的方法也有两种。

利用 JSON

const clonePerson = JSON.parse(JSON.stringify(person))
复制代码

JSON

从图中也能看出来,利用 JSON 的方法也是会有很多缺点的。

缺点1:会忽略 undefined

缺点2:不能序列化函数

缺点3:无法拷贝 Symbol

递归拷贝

递归拷贝其实也就是在浅拷贝的遍历拷贝上新增了一些东西

const deepClone = function (target) {
    if (typeof target !== 'object') {
        throw new TypeError('arguments must be a Object!')
    }
    let obj = {}
    // 设置原型
    const prototype = Reflect.getPrototypeOf(target)
    Reflect.setPrototypeOf(obj, prototype)
    // 设置属性
    Reflect.ownKeys(target).forEach((key) => {
        const value = target[key]
        if (value !== null && typeof value === 'object') {
            obj[key] = deepClone(value)
        } else {
            obj[key] = value
        }
    })
    return obj
}
复制代码

递归拷贝

达到了想要的效果。

猜你喜欢

转载自juejin.im/post/5b5d3f54e51d453467551604