对象拷贝的意义
保证原数据的完整性和独立性
常见场景:复制数据、函数入参等等
对象拷贝分为浅拷贝和深拷贝
浅拷贝
只拷贝对象的第一层级
如果属性值是原始数据类型,拷贝其值
如果属性值是引用类型,拷贝其内存地址
常见的浅拷贝方式: 对象:ES6拓展运算符、Object.assign、使用遍历复制 数组:ES6拓展运算符、slice、[].concat、Array.from
const person = {
name:"John",
age:18,
getName:function() { return this.name; },
address:{
province:"上海"
}
}
const person2 = {...person};
person2.name = "Hedy"
person2.getName = function () {
return 'person2'+this.name;
}
person2.address.province = "成都";
console.log(person.name,person.getName.toString(),person.address.province)
// John function() { return this.name; } 成都
可以看到只有对象内属性受影响,函数不会受影响,函数是给了个新的引用地址
深拷贝
克隆对象的每个层级
如果属性值是原始数据类型,拷贝其值
如果属性值是引用数据类型,递归克隆
常见方法
JSON.parse(JSON.stringify())
优点:简单方便
缺点:
1、只能复制普通键的属性,不能复制Symbol类型的属性
2、循环引用对象不能复制,比如window
3、函数、Date、Rege、Blob等数据类型也不能复制,Date被解析成字符串,其他会返回空对象
4、性能差,可能爆栈
利用消息通信来实现深度复制
常用的消息通信方式:window.postMeaasge、BoradcastChannel、SharedWorker、MessageChannel
优点:这些都是H5自带实现的,使用相对简单
缺点:
1、循环引用的对象不能复制,如window
2、函数不能复制
3、同步变成异步
// 使用BoradcastChannel
function clone(data) {
// 如果多次调用这里的name需要唯一
const name = "__clone__";
const ch1 = new BroadcastChannel(name);
const ch2 = new BroadcastChannel(name);
return new Promise((resolve) => {
ch2.addEventListener('message',ev=>resolve(ev.data))
ch1.postMessage(data);
})
}
// 使用postMessage
function clone(data) {
return new Promise((resolve) => {
window.addEventListener('message',ev=>resolve(ev.data))
window.postMessage(data);
})
}
clone(person).then((data)=>{
const person2 = data;
person2.name = "Hedy"
person2.getName = function () {
return 'person2'+this.name;
}
person2.address.province = "成都";
console.log(person.name,person.address.province)
注释掉函数正常执行打印出:John 上海
我之前在使用postMessage的时候还会使用JSON.parse(JSON.stringify())深度复制一下数据,其实是不用的
手写实现
要注意的点:
循环引用
递归爆栈
特殊类型
可以参照jsmini源码:github.com/jsmini/clon…
深浅拷贝需要根据自己的实际业务场景去选择,如果知道只有一层数据且都是基础数据类型,就可以使用浅拷贝