什么是深浅拷贝
- 拷贝 顾名思义就是复制,把一个值赋值给另外一个值,那为什么还分深浅呢?
- 首先我们得知道在js中,基本数据类型是保存在栈当中,而引用类型是保存在堆当中(指针在栈中)。
一般的原始类型赋值操作就是深拷贝,所以深拷贝和浅拷贝通常只针对引用类型来讨论。
浅拷贝
浅拷贝: 拷贝原对象,新对象会受原对象修改的影响
let obj1 = {
name:'zxc',
like:{
a:'dance',
b:'sing'
}
}
let obj2 = obj1
obj1.name= 'asd'
console.log(obj2);
这就是浅拷贝了,当修改obj1里面的值之后,obj2 里面的值也会改变
常见的浅拷贝方法也有许多: Object.assign(),Object.create(), {...obj}, slice(0), concat(),[...arr]等等
例如:
let obj1 = {
name:'zxc',
like:{
a:'dance',
b:'sing'
}
}
const obj2 = Object.assign({},obj1)
obj1.name= 'asd'
obj1.like.a = rap
console.log(obj2);
这里使用了Object.asign来实现浅拷贝,可以看出obj1的name属性被修改了但是obj2的name并没有修改,然而obj1里面的like对象里面的a属性被修改了,所以这里还是浅拷贝,毕竟并不是所有的属性都不会被影响。浅拷贝比较简单,下面来聊聊深拷贝。
深拷贝
深拷贝: 拷贝原对象,新对象不会会受原对象修改的影响
深拷贝方法主要就是 JSON.parse(JSON.stringify())
var obj = {
1 : 1,
a :undefined,
b :function(){},
2 :[],
c :{
d : 2,
e : 3
},
}
let newObj = JSON.parse(JSON.stringify(obj))
console.log(newObj);
从打印结果可以看出: 少了一些东西, a :undefined,b :function(){}, 这两个东西好像没有了,这是为什么呢?
该方法是用JSON.parse将对象转为字符串,然后在用JSON.stringify转回对象json字符串转换为对象的时候,会自己去构建新的内存地址存放数据,但是不能拷贝undefined,Symbol,function,正则,NaN,Infinity,Date以及不能处理循环引用。
让我们来实现一个简陋的深拷贝
let obj = {
name:'ggg',
like:{
a:'ctrl',
b:'sing'
}
}
let newObj = deepClone(obj)
function deepClone(obj) {
if (obj === null) return null // 判断要拷贝的对象是不是null
if (obj instanceof RegExp) return new RegExp(obj) // 判断要拷贝的是否为正则,是的话直接返回一个正则的实例对象
if (obj instanceof Date) return new Date(obj) //判断要拷贝的是否为Date类型,是的话直接返回Date的实例对象
let newObj = Array.isArray(obj) ? [] : {}//调用数组的API,来判断要拷贝的对象的类型是数组类型还是对象类型
for (let key in object) { // 遍历这个obj对象,然后判断key对应的value是什么类型。
if (Object.hasOwnProperty(key)) {//key是否是obj显示具有
newObj[key] = typeof obj[key] === 'object' ? deepClone(obj[key]) : obj[key]
//如果是原始类型就直接赋值,如果得到的是一个对象那就递归的去判断 所得到的对象里面的 属性,直到判断到原始类型 赋值之后才会跳出来。
}
return newObj
}
}
至此就实现了一个很简陋的深拷贝。
另一种深拷贝
let obj = {
name: 'ggg',
like: {
a: 'ctrl',
b: 'sing'
}
}
//管道消息通信
function deepClone(obj) {
return new Promise((resolve, reject) => { //返回一个 Promise。该 Promise 在克隆的对象可用 resolve。
const { port1, port2 } = new MessageChannel() //创建一个消息通道
port1.postMessage(obj) //将对象发送到通道的第一个端口
port2.onmessage = (msg) =>{ //在通道的第二个端口上注册一个处理程序,该处理程序将通过第一个端口的克隆对象作为消息接收。
resolve(msg.data) // 可以在调用这个函数的时候接 .then()
}
})
}
deepClone(obj).then(res => {
console.log(res);
})
这种方法也可以实现深拷贝。原理是:消息通道可以用于在两个线程之间传递数据。在这种情况下,消息通道用于在主线程和副线程之间传递数据。主线程将对象发送到消息通道的第一个端口,副线程在通道的第二个端口上接收对象,并将其克隆。
总结
以上就是一些对于深浅拷贝的理解,有其他的方法和修改建议,欢迎评论区提出和指正。