JavaScript之structuredClone现代深拷贝

在JavaScript中,实现深拷贝的方式有很多种,每种方式都有其优点和缺点。今天介绍一种原生JavaScript提供的structuredClone实现深拷贝。

下面列举一些常见的方式,以及它们的代码示例和优缺点:

1. 使用JSON.parse(JSON.stringify(obj))

代码示例:

复制代码
function deepClone(obj) {
    return JSON.parse(JSON.stringify(obj));
}

优点:简单易行,对于大多数对象类型有效。

缺点:不能复制原型链,对于包含循环引用的对象可能出现问题。比如以下代码:

复制代码
const calendarEvent = {
  date: new Date()
}

const problematicCopy = JSON.parse(JSON.stringify(calendarEvent))

最终得到的date不是Data对象,而是字符串。

复制代码
{
    "date": "2024-03-02T03:43:35.890Z"
}

这是因为JSON.stringify只能处理基本的对象、数组。任何其他类型都没有按预期处理。例如,日期转换为字符串。Set/Map只是转换为{}。

复制代码
const kitchenSink = {
  set: new Set([1, 3, 3]),
  map: new Map([[1, 2]]),
  regex: /foo/,
  deep: { array: [ new File(someBlobData, 'file.txt') ] },
  error: new Error('Hello!')
}

const veryProblematicCopy = JSON.parse(JSON.stringify(kitchenSink))

最终得到如下数据:

复制代码
{
  "set": {},
  "map": {},
  "regex": {},
  "deep": {
    "array": [
      {}
    ]
  },
  "error": {},
}

2. 使用递归

代码示例:

复制代码
function deepClone(obj) {
    if (obj === null || typeof obj !== 'object') {
        return obj;
    }
    let clone = obj.constructor();
    for (let attr in obj) {
        if (obj.hasOwnProperty(attr)) {
            clone[attr] = this.deepClone(obj[attr]);
        }
    }
    return clone;
}

优点:对于任何类型的对象都有效,包括循环引用。

缺点:对于大型对象可能会消耗大量内存,并可能导致堆栈溢出。

3. 第三方库,如 lodash 的 _.cloneDeep 方法

代码示例:

复制代码
const _ = require('lodash');
function deepClone(obj) {
    return _.cloneDeep(obj);
}

优点:支持更多类型的对象和库,例如,支持 Proxy 对象。

缺点:会引入依赖导致项目体积增大。

因为这个函数会导致17.4kb的依赖引入,如果只是引入lodash会更高。

4. 现代深拷贝structuredClone

在现代浏览器中,可以使用 structuredClone 方法来实现深拷贝,它是一种更高效、更安全的深拷贝方式。

以下是一个示例代码,演示如何使用 structuredClone 进行深拷贝:

复制代码
const kitchenSink = {
  set: new Set([1, 3, 3]),
  map: new Map([[1, 2]]),
  regex: /foo/,
  deep: { array: [ new File(someBlobData, 'file.txt') ] },
  error: new Error('Hello!')
}
kitchenSink.circular = kitchenSink

const clonedSink = structuredClone(kitchenSink)

structuredClone可以做到:

  • 拷贝无限嵌套的对象和数组
  • 拷贝循环引用
  • 拷贝各种各样的JavaScript类型,如Date、Set、Map、Error、RegExp、ArrayBuffer、Blob、File、ImageData等

哪些不能拷贝:

  • 函数
  • DOM节点
  • 属性描述、setter和getter
  • 对象原型链

所支持的完整列表:

Array、ArrayBuffer、Boolean、DataView、Date、Error类型(下面具体列出的类型)、Map、Object,但仅限于普通对象、原始类型,除了symbol(又名number、string、null、undefined、boolean、BigInt)、RegExp、Set、TypedArray

Error类型:

Error, EvalError, RangeError, ReferenceError , SyntaxError, TypeError, URIError

Web/API类型:

AudioData, Blob, CryptoKey, DOMException, DOMMatrix, DOMMatrixReadOnly, DOMPoint, DomQuad, DomRect, File, FileList, FileSystemDirectoryHandle, FileSystemFileHandle, FileSystemHandle, ImageBitmap, ImageData, RTCCertificate, VideoFrame

值得庆幸的是 structuredClone 在所有主流浏览器中都受支持,也支持Node.js和Deno。

最后

我们现在终于可以直接使用原生JavaScript中的structuredClone能力实现深度拷贝对象。每种方式都有其优缺点,具体使用方式取决于你的需求和目标对象的类型。

技术前沿拓展

前端开发,你的认知不能仅局限于技术内,需要发散思维了解技术圈的前沿知识。细心的人会发现,开发内部工具的过程中,大量的页面、场景、组件等在不断重复,这种重复造轮子的工作,浪费工程师的大量时间。

介绍一款程序员都应该知道的软件JNPF快速开发平台,很多人都尝试用过它,它是功能的集大成者,任何信息化系统都可以基于它开发出来。

这是一个基于 Java Boot/.Net Core 构建的简单、跨平台快速开发框架。前后端封装了上千个常用类,方便扩展;集成了代码生成器,支持前后端业务代码生成,实现快速开发,提升工作效率;框架集成了表单、报表、图表、大屏等各种常用的 Demo 方便直接使用;后端框架支持 Vue2、Vue3。如果你有闲暇时间,可以做个知识拓展。

看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~

相关推荐
小朱爱编程1232 分钟前
我用 Jev 做了三个实用工具:整理标签页、分诊飞书反馈、找回 GitHub 收藏
java·开发语言·人工智能·后端·python·架构·ai编程
李游Leo4 分钟前
HarmonyOS 7 ArkTS + ohpm:三方 JS 库副作用隔离与 API 26 升级回归【鸿蒙心迹】
javascript·回归·harmonyos
你挚爱的强哥9 分钟前
【sgKeyboard】自定义组件:虚拟键盘
前端·javascript·计算机外设
锅里游的鱼吖15 分钟前
echarts自定义折线图
前端·javascript·数据库
谢亮_vipxieliang27 分钟前
Java 8/11/17/21/25 怎么选?一篇讲清 LTS 升级路线
java·开发语言
Cindy_cd35 分钟前
IDEA2021 配置 JDK+Maven
java·开发语言·maven
何何____1 小时前
Vue 全局事件总线详解
前端·javascript·vue.js
mmsx2 小时前
Android Dialog 插槽模式:用一套壳统一全应用的弹窗风格
android·java·dialog·开发语言
huanbailu2 小时前
Qt 跨线程信号槽:connect 返回 true 但槽函数不执行的坑
开发语言·c++·qt·信号槽·跨线程·元类型
u0111026752 小时前
工具页案例图如何编写 alt让图片说明与处理场景对应
java·前端·javascript·图像处理·人工智能·算法·ai作画