深拷贝浅拷贝是一道常见的面试。
浅拷贝
- 直接赋值
let a = {
age: 1
}
let b = a;
console.log(b) // {age: 1}
a.age = 2;
console.log(b) // {age: 2}
弊端:对象在赋值的过程中其实是复制了地址,从而导致改变了一方其他也都被改变的情况。
Object.assign方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
Object.assign方法的第一个参数是目标对象,后面的参数都是源对象。
let a = {
age: 1
}
let b = Object.assign({}, a);
a.age = 2;
console.log(b.age) // 1
可以看到通过Object.assign() 可以解决直接赋值出现的问题;
- 展开运算符 …
let a = {
age: 1
}
let b = {...a};
a.age = 2;
console.log(b.age) // 1
深拷贝
通常浅拷贝就能解决大部分问题了,但是当我们遇到如下情况就需要用到深拷贝了。
let a = {
age: 1,
person:{
tall: 180,
}
}
let b = {...a}
a.person.tall = 170;
console.log(b.person.tall)
浅拷贝只解决了第一层的问题,如果值中海油对象的话,那么又回到了最开始的话题了,两者享有同样的地址,这种情况下就需要用到深拷贝了。
- JSON.parse(JSON.stringify(obj))
let a = {
age: 1,
person:{
tall: 180,
}
}
let b = JSON.parse(JSON.stringify(a))
a.age = 2;
console.log(b)
弊端:
- 会忽略undefined
- 会忽略symbol
- 不能序列化函数
- 不能解决循环引用的对象
- 循环引用,也不能通过该方法实现深拷贝
- 在遇到函数、 undefined 或者 symbol 的时候,该对象也不能正常的序列化
// 有undefined + 循环引用
let obj = {
a: 1,
b: {
c: 2,
d: 3,
},
f: undefined
}
obj.c = obj.b;
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
function deepCopy(obj) {
return new Promise((resolve) => {
const {port1, port2} = new MessageChannel();
port2.onmessage = ev => resolve(ev.data);
port1.postMessage(obj);
});
}
deepCopy(obj).then((copy) => { // 请记住`MessageChannel`是异步的这个前提!
let copyObj = copy;
console.log(copyObj, obj)
console.log(copyObj == obj)
});
但拷贝有函数的对象时,还是会报错,这个时候就要考虑用 lodash 的深拷贝函数了