JS知识点回顾——对象的拷贝

128 阅读2分钟

对象拷贝的意义

保证原数据的完整性和独立性

常见场景:复制数据、函数入参等等

对象拷贝分为浅拷贝和深拷贝

浅拷贝

只拷贝对象的第一层级

如果属性值是原始数据类型,拷贝其值

如果属性值是引用类型,拷贝其内存地址

常见的浅拷贝方式: 对象: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、性能差,可能爆栈

image.png

利用消息通信来实现深度复制

常用的消息通信方式: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)

image.png

注释掉函数正常执行打印出:John 上海

我之前在使用postMessage的时候还会使用JSON.parse(JSON.stringify())深度复制一下数据,其实是不用的

手写实现

要注意的点:

循环引用

递归爆栈

特殊类型

可以参照jsmini源码:github.com/jsmini/clon…

深浅拷贝需要根据自己的实际业务场景去选择,如果知道只有一层数据且都是基础数据类型,就可以使用浅拷贝

image.png