浅拷贝
Object.assign()只会拷贝所有属性的值到新的对象中,如果属性值是对象的话,拷贝的是地址,所以并不是深拷贝
let a ={
age:1
};
let b = Object.assign({},a);
a.ge = 2;
console.log(b.age)//1
另外还可以通过展开运算符...来实现浅拷贝
let a ={
age:1
}
let b ={...a};
a.age = 2;
console.log(b.age)//1
通常浅拷贝能解决大部分问题,但是当我们遇到如下情况可能就需要用到深拷贝了
let a ={
age:1,
msg:{
name:'zhangsan'
}
}
let b = {...a};
a.msg.name = 'lisi';
console.log(b.msg.name)//lisi
深拷贝
这个问题通常可以用JSON.pare(JSON.stringify(Object)) 来解决
let a ={
age:1,
msg:{
name:'zhangsan'
}
}
let b = JSON.parse(JSON.stringify(a));
a.msg.name = 'lisi'
console.log(b.msg.name)//zhangsan
但是该方法也是有局限性
- 会忽略 undefined
- 会忽略symbol
- 不能序列化函数
- 不能解决循环引用的对象
let obj ={
a:1,
b:{
c:2,
d:3
}
}
obj.c = obj.a
obj.e = obj.a
obj.b.c = obj.c
obj.b.d = obj.b
obj.b.e = obj.b.c
let newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj)//报错
当遇到循环引用对象时,该方法会出现报错
当遇到函数、undefined或者symbol的时候,该对象也不能正常序列化
let a ={
age:undefined,
sex:symbol,
jobs:function(){},
name:'zhangsan'
}
let b = JSON.parse(JSON.stringify(a))
console.log(b)//{name:'zhangsan'}
在通常情况下,这个函数可以解决大部分问题
如果你所需拷贝的对象包含内置类型并且不包含函数,可以使用 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
}
}
obj.b.d = obj.b
// 注意该方法是异步的
// 可以处理 undefined 和循环引用对象
const test = async () => {
const clone = await structuralClone(obj)
console.log(clone)
}
test()
手写简易版,更推荐使用lodash 的深拷贝函数
function deepClone(obj){
function isObject(o){
return (typeof o === 'object' || typeof o == 'function') && o !== null;
}
if(!isObject(obj)){
throw new Errow('非对象')
}
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
}
}
let newObj = deepClone(obj)
newObj.b.c = 1
console.log(obj.b.c) // 2