浅谈浅拷贝与深拷贝
浅谈浅拷贝与深拷贝
我们知道对象是一种引用数据类型,当我们将一个对象赋值给另一个对象时,实际上是把对象的地址引用过去,当原对象的属性发生变化时,其他引用它的对象的属性也会发生变化;同样的当引用它的对象的属性发生变化时,原对象的属性也会发生变化。
let a = {
age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2
b.age = 3
console.log(b.age) // 3
console.log(a.age) // 3
但是实际使用过程中我们往往不希望这样,所以就要讨论到浅拷贝和深拷贝的问题:
1、浅拷贝
Object.assign
Object.assign 只会拷贝所有的属性值到新的对象中,如果属性值是对象的话,拷贝的是地址,所以并不是深拷贝。
let a = {
age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1
···运算符
另外我们还可以通过展开运算符 ... 来实现浅拷贝
let a = {
age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1
2、深拷贝
通常浅拷贝就能解决大部分问题了,但是当我们遇到如下情况就可能需要使用到深拷贝了
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = { ...a }
a.jobs.first = 'native'
console.log(b.jobs.first) // native
JSON.parse(JSON.stringify(object))
当拷贝的对象属性中是一些普通对象时可以使用JSON.parse(JSON.stringify(object))
let a = {
age: 1,
jobs: {
first: 'FE'
}
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE
但是这种方法也有局限性:
- 会忽略掉
undefined - 会忽略
symbol - 不能序列化函数
- 不能解决循环引用的对象
let a = {
age: undefined,
sex: Symbol('male'),
jobs: function() {},
name: 'cxk'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b) // {name: "cxk"}
MessageChannel
如果你所需拷贝的对象含有内置类型并且不包含函数,可以使用 MessageChannel
function structuralClone(obj) {
return new Promise(resolve => {
const { port1, port2 } = new MessageChannel()
port2.onmessage = ev => resolve(ev.data)
port1.postMessage(obj)
})
}
var obj = {
a: 1,
b: {
c: 2
},
d: undefined
}
obj.b.d = obj.b
// 注意该方法是异步的
// 可以处理 undefined 和循环引用对象
const test = async () => {
const clone = await structuralClone(obj)
console.log(clone)
}
test()
该方法可以处理循环引用对象和undefined,但是依然不能处理函数
简易版深拷贝
function deepClone(obj) {
function isObject(o) {
return (typeof o === 'object' || typeof o === 'function') && o !== null
}
if (!isObject(obj)) {
throw new Error('非对象')
}
let isArray = Array.isArray(obj)
let newObj = isArray ? [...obj] : { ...obj }
Reflect.ownKeys(newObj).forEach(key => {
newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
})
return newObj
}
let obj = {
a: [1, 2, 3],
b: {
c: 2,
d: 3
},
e: undefined,
f: function aa(){},
g: Symbol('bb')
}
let newObj = deepClone(obj)
newObj.b.c = 1
console.log(obj.b.c) // 2
该方法仅仅是深拷贝的简单实现方法,依然不能考虑到很多边界情况,但是这已经能满足绝大多数使用情况,想要了解更深入的深拷贝,建议百度lodash的深拷贝函数。

浙公网安备 33010602011771号