Js对象复制的各种姿势(未完成版)

2,537 阅读2分钟

这个问题来自于在对koa源码的解析之中被再次提起。 源码的位置 github.com/koajs/koa/b…

程序中需要从context,request,response模板中复制三个对象,并将其互相挂载起来。这里面用到了Object.create方法。

首先我们先盘一盘如果想达到对象复制都有哪几种办法

为什么我们要复制对象

首先我们看看为什么要复制变量,假设我们用字面量定义一个模板对象, 如果不复制,两个对象其实是引用的同一个对象实例。

const template = {
    data: 1,
}

const objectA = template
template.data = 8
const objectB = template
template.data = 9

console.log('objectA:',objectA)
console.log('objectB:',objectB)

// 运行结果
// objectA: { data: 9 }
// objectB: { data: 9 }

`121

方法一: 解构赋值

解构赋值其实就是利用解构的浅拷贝效应从而达到变量赋值的目的。当然既然是浅拷贝,深层就不行了。

const template = {
    data: 1,
    info: {
        b: 1
    }
}

const objectA = {...template}
objectA.data = 2
objectA.info.b = 8
const objectB = {...template}
objectB.data = 3
objectB.info.b = 9

console.log('objectA:',objectA)
console.log('objectB:',objectB)

// 运行结果
// ============
// objectA: { data: 2, info: { b: 9 } }
// objectB: { data: 3, info: { b: 9 } }

方法二: JSON.stringify & parse

为了解决深层拷贝问题我们可以考虑将源对象转换为字符串,再转换为新对象。

const template = {
    data: 1,
    info: {
        b: 1
    }
}

const objectA = JSON.parse(JSON.stringify(template))
objectA.data = 2
objectA.info.b = 8
const objectB = JSON.parse(JSON.stringify(template))
objectB.data = 3
objectB.info.b = 9

console.log('objectA:',objectA)
console.log('objectB:',objectB)

// 运行结果
// ============
// objectA: { data: 2, info: { b: 8 } }
// objectB: { data: 3, info: { b: 9 } }

方法三:Object.create

看似问题解决了 但是如果我们如果使用了getter和setter方法的话,而且里面又有一个字面量中没有描述的对象,问题就出现了,由于上面两种复制方式都会调用到变量的getter方法,这个时候就会报错。

const template = {
    data: 1,
    info: {
        b: 1
    },
    get c() {
        return this.xxx.data
    }
}

实际上Object.create是做了以对象为原型进行了继承操作 getter/setter方法可以继承过来 但是并不能完成深层copy

const template = {
    data: 1,
    info: {
        b: 1
    },
    get c() {
        return this.xxx.data
    }
}

const objectA = Object.create(template)
objectA.data = 2
objectA.info.b = 8
const objectB = Object.create(template)
objectB.data = 3
objectB.info.b = 9

console.log('objectA:',objectA.__proto__)
console.log('objectA的原型:',objectA.__proto__)
console.log('objectA.data:',objectA.data)
console.log('objectA.info.b:',objectA.info.b)
console.log('objectB的原型:',objectB.__proto__)
console.log('objectB.data:',objectB.data)
console.log('objectB.info.b:',objectB.info.b)

// 运行结果
// ============
// objectA: { data: 1, info: { b: 9 }, c: [Getter] }
// objectA的原型: { data: 1, info: { b: 9 }, c: [Getter] }
// objectA.data: 2
// objectA.info.b: 9
// objectB的原型: { data: 1, info: { b: 9 }, c: [Getter] }
// objectB.data: 3
// objectB.info.b: 9 // 并没有深拷贝

归纳总结

赋值 浅拷贝 深拷贝 getter/setter
解构赋值
JSON.stringify
Object.create

最后我们就知道了在koa源码里面 主要就是要继承原型中的getter/setter所以选择了Object.create方法