关于JS的深拷贝一些使用总结

关于JS的深拷贝一些使用总结

原文地址:

https://mp.weixin.qq.com/s/g7t6UZH0Epq8_62jJFTFDg

先说概念

在JavaScript中,深拷贝一个对象是创建一个全新的对象,包括嵌套对象在内,所有属性都是完全独立的副本。这与浅拷贝不同,浅拷贝只会复制第一级属性,而嵌套的对象则是引用,而非复制。

工作使用情况

1、lodash:_.cloneDeep(value); (最常用,因为我们的项目有这个库)
2、原生 structuredClone() 方法; (用的少,只有个别独立小项目没有引入lodash库的时候才会用)

JSON.parse & JSON.stringify

老生长谈的一种深拷贝方式,JSON.parse(JSON.stringify(obj))是一种使用JSON序列化创建对象的深度拷贝的方法。(它适用于不包含任何循环引用的对象,但如果对象包含循环引用,则该方法会失败)

!缺点:目前已知的碰过的,如果拷贝对象中含有Date对象的,他会把日期转换为一个字符串,不是Date对象了

Lodash 或 underscore _.cloneDeep()

到目前为止,Lodash的cloneDeep函数一直是解决这个问题的常见方法。好用,实用,没什么问题

!缺点:容易滥用
1、引入包,如果只是单纯用这个,就要拉入lodash库,无形增加项目包的体积
2、在处理大型、复杂的对象时可能会导致性能问题,因为它们需要递归地遍历整个对象,并复制每个属性和嵌套对象。在处理大型对象时,这可能会导致严重的性能问题。
3、无法复制一些特定的对象,如 Blob、File、ImageData 和 AudioContext 等

常见错误

1、使用展开运算符来克隆
2、使用 Object.assign来克隆

解决方法

结构化克隆(Structured Clone)✅

结构化克隆是 JavaScript 中的一个概念,它指的是创建对象的深层克隆过程,包括其嵌套对象、数组以及其他复杂数据结构。结构化克隆算法被 JavaScript 中多个 API 所使用,例如用于窗口对象(例如页面和 iframe 之间)之间通信的 postMessage 方法,用于在浏览器中存储和检索数据的 IndexedDB API,以及用于后台处理网络请求的 Service Workers API。

结构化克隆算法通过递归地复制对象的所有属性,包括嵌套对象和数组,将其复制到具有相同结构和值的新对象中。它可以处理多种数据类型,包括函数、日期、正则表达式、数组和对象。

结构化克隆算法是在 JavaScript 中创建复杂数据结构的深层克隆的强大工具,并经常用于在浏览器或其他 JavaScript 环境中执行复杂的数据操作。

1、手写代码实现

使用递归自定义实现 —— 这种方法涉及定义一个使用递归创建对象的深层克隆的函数。

下面展示一些 内联代码片

const original = { a: 1, b: { c: new Date() } };

const deepClone = (obj) => {
  if (obj === null || typeof obj !== "object") return obj;
  let clone = Array.isArray(obj) ? [] : {};
  for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] =
        obj[key] instanceof Date
          ? new Date(obj[key].getTime())
          : deepClone(obj[key]);
    }
  }
  return clone;
};

const clone = deepClone(original);

console.log(clone);
// Output: {a: 1, b: {c: 2022-12-31T08:00:00.000Z}}

clone.b.c.setFullYear(2023);

console.log(original);
// Output: {a: 1, b: {c: 2022-12-31T08:00:00.000Z}}

此方法可以解决上述所有问题。以下是上述代码解读:

首先,我们定义了一个原始对象 original,它包含两个属性 a 和 b,其中 b 属性的值是一个日期对象。
下面展示一些 内联代码片

const original = { a: 1, b: { c: new Date() } };

接下来,定义了一个 deepClone 函数,用于对任意对象进行深拷贝。这个函数首先检查传入的对象是否是 null 或非对象,如果是,则直接返回该对象。

const deepClone = (obj) => {
  if (obj === null || typeof obj !== "object") return obj;

接着,根据传入对象的类型创建一个克隆对象,如果传入的是数组,则创建一个空数组,否则创建一个空对象。

let clone = Array.isArray(obj) ? [] : {};

然后,遍历原始对象的所有属性,如果该属性是原始对象自身的属性(不是原型链上的属性),则递归地对该属性进行深拷贝,并将其赋值给克隆对象的对应属性。如果该属性是日期对象,则对其进行特殊处理,创建一个新的日期对象,并将其时间戳设置为原始日期对象的时间戳。

for (const key in obj) {
    if (obj.hasOwnProperty(key)) {
      clone[key] =
        obj[key] instanceof Date
          ? new Date(obj[key].getTime())
          : deepClone(obj[key]);
    }
  }
  //最后,返回克隆对象。

  return clone;
};

最后做下验证,使用 deepClone 函数对 original 对象进行深度拷贝,得到了一个新的克隆对象 clone。在 clone 对象上进行修改并不会影响原始对象 original。

const clone = deepClone(original);

console.log(clone);
// Output: {a: 1, b: {c: 2022-12-31T08:00:00.000Z}}

clone.b.c.setFullYear(2023);

console.log(original);
// Output: {a: 1, b: {c: 2022-12-31T08:00:00.000Z}}

运行结果表明,尽管对 clone.b.c 进行了修改,但是原始对象 original 并没有受到影响,它的属性 b.c 仍然是一个日期对象,年份为 2022 年。

2、原生 structuredClone() 方法

structuredClone() 是 JavaScript 的原生方法,它是浏览器内置的 API 之一,可用于深拷贝对象。structuredClone() 方法在复制对象时会处理复杂的数据类型,并确保它们的引用关系得到正确处理。它是深拷贝对象的最佳选择之一,因为它可以同时处理各种数据类型,并保持它们的引用关系不变。此外,它还支持多种环境,包括 Web Workers 和 Service Workers 等。

使用structuredClone()进行深拷贝的优点是它是 JavaScript 的原生方法,内置于浏览器中,因此不需要额外的依赖。此外,它能够深度复制包括 Date,RegExp,Map 和 Set 等所有 JavaScript 基本类型和复杂类型数据,能够准确地复制各种对象和数据结构。在需要精确拷贝数据的场景中,如跨文档通信、存储和恢复应用程序状态等场景,使用该方法可以确保复制的对象完全相同,不会出现副作用。

然而,使用structuredClone()进行深拷贝的缺点是它只能用于浏览器环境,不能用于 Node.js 环境。此外,它不能复制一些特定的对象,如 Blob,File,ImageData,AudioContext 等。还有一点需要注意的是,由于该方法是基于序列化和反序列化实现的,所以它可能不适用于大型对象和数据结构,因为它可能会导致性能问题。

因此,在使用structuredClone()方法进行深拷贝时,需要根据具体情况权衡其优缺点,并确保使用它的场景是合适的。

手写方法和原生方法的比较

对于大多数JavaScript对象和数据结构来说,structuredClone()是更好的深拷贝方法。因为它能够正确处理复杂的数据结构,包括日期、正则表达式等,而且可以支持跨文档、跨窗口或者跨 Worker 进行数据传输。另一方面,手写自定义递归实现深拷贝方法可以满足特定的需求,比如需要处理特定的数据类型或者需要更高的性能。但是需要注意的是,这种方法也可能会有一些缺陷和局限性,比如对于循环引用的处理可能不太好会有性能问题。因此,在选择深拷贝方法时需要根据具体情况进行选择。

延伸阅读

对于Blob,File,ImageData和AudioContext对象,这些对象本身是不支持序列化的,所以无法通过structuredClone()来深拷贝。因此,在这种情况下,你需要使用其他方法来拷贝这些对象。

对于Blob和File对象,你可以使用FileReader API或Blob API来复制这些对象。对于ImageData对象,你可以使用Canvas API来复制它。对于AudioContext对象,你可以使用Web Audio API中的其他方法来复制它。

需要注意的是,这些方法可能会比使用structuredClone()更加复杂,但是它们可以处理structuredClone()不能处理的对象类型。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值