JS:如何实现对象的拷贝

拷贝指的是将一个对象的内容复制到另一个对象中。在JavaScript中,拷贝指的是将一个对象的内容复制到另一个对象中,拷贝分为浅拷贝和深拷贝且通常只针对引用类型。浅拷贝只拷贝一层对象,它会复制原始值以及引用类型数据的指针。而深拷贝会层层拷贝,它会将所有类型的属性值都会被复制。

浅拷贝

浅拷贝是指通过方法创建一个新对象把某个对象完整拷贝,拷贝后的新对象中的每个元素都是原始对象中相应元素的引用,即原对象的修改会影响新的对象。

在JavaScript中,原始类型的值直接存在调用栈中而引用类型的值是存放在堆中的,引用类型在调用栈里存放的只是堆中的引用地址。所以当对原始类型进行拷贝时,实际上是将原始值从一个变量复制到另一个变量,而不涉及引用。

常见的浅拷贝方法

  • Object.create(obj) // 创建一个对象
css 复制代码
let a = {
    name:'张三'
}
let b =Object.create(a)
a.name='李四'

console.log(b.name); //输出: 李四
  • Object.assign({}, obj) // 对象拼接方法
css 复制代码
let a ={
    name:'张三',
    info:{
        age:18
    }
}
let b=Object.assign({},a)
a.info.age=20

console.log(b)//输出: { name: '张三', info: { age: 20 } }
  • \[\].concat(arr) // 数组拼接方法
ini 复制代码
let arr = [1,2,3,{n:10}]
let newArr = [].concat(arr)
arr[3].n =100

console.log(newArr);
  • 数组解构
ini 复制代码
let arr = [1,2,3,{n:10}]
let newArr=[...arr]
arr[3].n =100

console.log(newArr);
  • slice() //数组剪切方法
ini 复制代码
const arr = [1, 2, 3, 4, 5];
const newArr = arr.slice();

console.log(newArr); // [1, 2, 3, 4, 5]

浅拷贝的实现

ini 复制代码
let obj={
    a:1,
    b:{
        n:2
    },
    c:[1,2,3,4]
}

let obj2 = shallowCopy(obj);

function shallowCopy(obj) {
    let obj2 = {};
    for (let key in obj) {
        if(obj.hasOwnProperty(key)){
            obj2[key] = obj[key];
        }
    } 
    return obj2;
}

obj2.b.n=100
console.log(obj); // 输出为:{ a: 1, b: { n: 100 }, c: [ 1, 2, 3, 4 ] }
console.log(obj2);// 输出为:{ a: 1, b: { n: 100 }, c: [ 1, 2, 3, 4 ] }

深拷贝

深拷贝是指通过方法创建一个新对象把某个对象完整拷贝,拷贝后的新对象中的内容是复制的原始对象的内容,新对象中的每个元素都是原始对象中相应元素的复制。所以修改新对象中的元素不会影响到原始对象中的对应元素。

常见的深拷贝方法

  • JSON.parse(JSON.stringify(obj))

当使用JSON.parse(JSON.stringify(obj)) 对对象进行深拷贝时,会将对象转换为 JSON 字符串,然后再将 JSON 字符串转换回对象,从而实现深层次的拷贝。

javascript 复制代码
let obj = {
    name: "张三",
    info: {
        age: 25,
        sex: "男"
    }
};

// 使用 JSON.parse(JSON.stringify(obj)) 进行深拷贝
let obj2 = JSON.parse(JSON.stringify(obj));

// 修改新对象中的属性的值
obj2.info.age=18;

// 输出原始对象和克隆后的对象
console.log(obj); // 输出: { name: '张三', info: { age: 25, sex: '男' } }
console.log(obj2); // 输出: { name: '张三', info: { age: 18, sex: '男' } }

值得注意的是:

  1. 这个方法不能处理underfined,function,Symbol 这些数据类型
  2. 也无法处理循环引用 。

这个方法可以使用大部分情况,如果遇到以上两种问题可以使用lodash的深拷贝函数


  • structuredClone(obj)
ini 复制代码
let obj = {
    a: 1,
    b: {
        n: 2
    },
    c: [1, 2, 3, 4]
}

const obj2=structuredClone(obj) 

structuredClone() 方法是用于复制一个对象,包括其所有属性和嵌套对象,而不会受限于像 JSON.stringify() 和 JSON.parse() 那样只能处理一部分 JavaScript 对象的限制。 值得注意的是structuredClone(obj)无法兼容symbol 和function。


深拷贝的实现

  • 递归
javascript 复制代码
let obj = {
  name: '张三',
  age: 18,
  a: {
    n: 1
  },
  b: undefined,
  c: null,
  d: function () { },
  e: Symbol('hello'),
  f: {
    n: 100
  }
}

function deepCopy(obj) {
  let objCopy = {}
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      //区分obj[key]是原始类型或者引用类型
      if (obj[key] instanceof Object) {// 不能直接赋值
        objCopy[key] = deepCopy(obj[key]) // 递归调用 
      } else {
        objCopy[key] = obj[key]
      }
    }
  }
  return objCopy
}

let obj2 = deepCopy(obj)
obj.age = 20;

console.log(obj2); 

通过遍历对象的属性,并利用递归来处理嵌套对象,从而实现了对象的深拷贝。

  • MessageChannel()
javascript 复制代码
let obj = {
    a: 1,
    b: {
        n: 2
    },
    c: [1, 2, 3, 4]
}

function deepCLone(obj) {
    return new Promise((resolve) => {
        const { port1, port2 } = new MessageChannel();
        port1.postMessage(obj)
        port2.onmessage = (msg) => {
            resolve(msg.data)
        }
    })
}

deepCLone(obj).then((res) => {
    obj2 = res
    console.log(obj2);
})

在这个函数中,使用 MessageChannel 创建了两个端口 port1port2,然后利用其中一个端口将对象 obj 发送给另一个端口。当另一个端口接收到消息时,将其作为深拷贝后的对象返回。通过 MessageChannel 实现的深拷贝可以处理对象包含循环引用的情况。

总结

需要注意的是,深拷贝会消耗更多的内存和时间,而在处理简单对象结构、不需要深层嵌套对象的情况下,浅拷贝是一个简单而有效的工具。总之,在进行拷贝操作时,需要根据实际情况选择合适的方式来确保程序的正确性和性能。

相关推荐
袅沫1 分钟前
Nginx中部署多个前端项目——区分路径
服务器·前端
meilindehuzi_a9 分钟前
Next.js 全栈基础实战:从 SPA、SSR 到 App Router 与 Todo API
开发语言·javascript·ecmascript
用户9210802628610 分钟前
6. 数据大屏 WebSocket 稳定性优化:心跳检测、断点续传和消息去重
前端
柚yuzumi10 分钟前
CSS 定位布局:让元素各就各位
前端·css
光影少年13 分钟前
react navite图片加载优化、大图卡顿、缓存策略
前端·react native·react.js
小林ixn13 分钟前
用 Next.js 和 Redis 撸一个 Markdown 笔记系统:RSC 实战与组件化拆解
前端·redis·next.js
渣波14 分钟前
重构旅行体验:基于 React 的 AI 旅游助手对话系统实战解析
前端·javascript
今日无bug17 分钟前
列表转树:一道题搞懂 HashMap 在算法里的价值
前端·数据结构
用户9210802628617 分钟前
5. 数据大屏实时通信第一步:为什么选择 WebSocket,以及如何接入 Socket.IO
前端
岁月留痕16818 分钟前
17 实战项目二:实现“日记”项目多页面管理
前端