当前位置: 首页 > news >正文

【你不知道的事】JavaScript 中用一种更先进的方式进行深拷贝:structuredClone

你是否知道,JavaScript中有一种原生的方法来做对象的深拷贝?

本文我们要介绍的是 structuredClone 函数,它是内置在 JavaScript 运行时中的:

const calendarEvent = {title: "Builder.io Conf",date: new Date(123),attendees: ["Steve"]
}const copied = structuredClone(calendarEvent)

在上面的示例中,我们不仅拷贝了对象,还拷贝了嵌套数组,甚至拷贝了Date对象:

copied.attendees // ["Steve"]
copied.date // Date: Wed Dec 31 1969 16:00:00
cocalendarEvent.attendees === copied.attendees // false

没错,structuredClone不仅可以做到以上这些,而且还可以:

  • 克隆无限嵌套的对象和数组
  • 克隆循环引用
  • 克隆各种各样的JavaScript类型,如Date, Set, Map, Error, RegExp, ArrayBuffer, Blob, File, ImageData等等
  • 转移任何可转移的对象

举个例子:

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)

为什么不使用对象扩展运算符进行克隆呢

值得注意的是,我们讨论的是深拷贝。如果你只需要做一个浅拷贝,也就是一个不复制嵌套对象或数组的拷贝,那么我们可以只做一个对象扩展:

const simpleEvent = {title: "前端修罗场",
}
const shallowCopy = {...calendarEvent}

或者你也可以使用这种方法:

const shallowCopy = Object.assign({}, simpleEvent)
const shallowCopy = Object.create(simpleEvent)

但是一旦我们有了嵌套项,我们就会遇到麻烦:

const calendarEvent = {title: "Builder.io Conf",date: new Date(123),attendees: ["Steve"]
}const shallowCopy = {...calendarEvent}shallowCopy.attendees.push("Bob")
shallowCopy.date.setTime(456)

如上所见,我们没有对该对象进行完整复制。

嵌套日期和数组仍然是两者之间的共享引用,如果我们想编辑它们,认为我们只是更新复制的日历事件对象,这可能会导致重大问题。

为什么不使用JSON.parse(JSON.stringify(x)) ?

它实际上是一个很棒的工具,性能令人惊讶,但也有一些structuredClone可以解决的缺点。

举个例子:

const calendarEvent = {title: "前端修罗场",date: new Date(123),attendees: ["Steve"]
}const problematicCopy = JSON.parse(JSON.stringify(calendarEvent))

如果我们输出 problematicopy,我们会得到:

{title: "前端修罗场",date: "1970-01-01T00:00:00.123Z"attendees: ["Steve"]
}

这不是我们想要的 date 格式,因为格式应该是date对象,而不是字符串。

这是因为 JSON.Stringify 只能处理基本对象、数组和基本类型。任何其他类型都可能以难以预测的方式处理。例如,日期被转换为字符串。但是 Set 对象就会被简单地转换为 {}。

同时,JSON.Stringify 甚至会完全忽略某些东西,如 undefined 或 function。

例如,如果我们用这个方法复制下面这个例子:

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": {},
}

可以看到, 这种方法的拷贝出错了。

因此,如果我们的需求适合这个方法,可以用这个方法。但是,我们可以用 structuredClone 做这个方法有很多不能做的事情。

为什么不是 _.cloneDeep?

到目前为止,Lodash 的 cloneDeep 函数是这个问题的一个非常常见的解决方案。事实上,这确实也像预期的那样工作:

import cloneDeep from 'lodash/cloneDeep'const calendarEvent = {title: "Builder.io Conf",date: new Date(123),attendees: ["Steve"]
}// ✅ 
const clonedEvent = structuredClone(calendarEvent)

但是,这里有一个警告。根据我的 IDE 中的导入成本扩展,打印任何我导入函数的成本,这个函数占了 17.4kb` 的大小(5.3kb gzip):

在这里插入图片描述
假设你只导入了这个函数。如果改用更常见的方式导入,没有意识到摇树并不总是按希望的方式工作,那么可能会无意中为这个函数导入高达2 5kb 的文件😱

在这里插入图片描述

什么是 structuredClone 克隆不了的

函数不能被克隆

structuredClone({ fn: () => { } }) // 会抛出一个 DataCloneError 异常

DOM 节点不能克隆

structuredClone({ el: document.body })// 会抛出一个 DataCloneError 异常

属性描述符 setter和getter 不能克隆

类似元数据的特性也不会被克隆。

例如,使用 getter,结果值会被克隆,但不会克隆 getter 函数本身(或任何其他属性元数据):

structuredClone({ get foo() { return 'bar' } })
// log: { foo: 'bar' }

对象属性不能被克隆

原型链不会被遍历或复制。因此,如果克隆MyClass的一个实例,克隆的对象将不再是该类的实例(但该类的所有有效属性将被克隆)

class MyClass { foo = 'bar' myMethod() { /* ... */ }
}
const myClass = new MyClass()const cloned = structuredClone(myClass)
// log: { foo: 'bar' }cloned instanceof myClass // false

structuredClone 支持类型的完整列表

更简单地说,任何不在下面列表中的东西都不能克隆:

  • JS 内置类型:Array, ArrayBuffer, Boolean, DataView, Date, Error types (those specifically listed below), Map , Object but only plain objects (e.g. from object literals), Primitive types, except symbol (aka number, string, null, undefined, boolean, BigInt), RegExp, Set, TypedArray

  • Error types: Error, EvalError, RangeError, ReferenceError , SyntaxError, TypeError, URIError

  • Web/API types: AudioData, Blob, CryptoKey, DOMException, DOMMatrix, DOMMatrixReadOnly, DOMPoint, DomQuad, DomRect, File, FileList, FileSystemDirectoryHandle, FileSystemFileHandle, FileSystemHandle, ImageBitmap, ImageData, RTCCertificate, VideoFrame

浏览器支持

所有主流浏览器都支持 structuredClone,甚至Node.js和Deno。

不过在 Web worker 中,目前支持是比较有限的。

在这里插入图片描述

相关文章:

【你不知道的事】JavaScript 中用一种更先进的方式进行深拷贝:structuredClone

你是否知道,JavaScript中有一种原生的方法来做对象的深拷贝? 本文我们要介绍的是 structuredClone 函数,它是内置在 JavaScript 运行时中的: const calendarEvent {title: "Builder.io Conf",date: new Date(123),attendees: ["Steve…...

XE开发Linux应用(二)-Webservice

新建一个工程。选择如图。继续输入服务名然后就生成对应的单元。增加linux 平台。完善对应的单元代码{ Invokable implementation File for Txaliontest which implements Ixaliontest }unit xaliontestImpl;interfaceuses Soap.InvokeRegistry, System.Types, Soap.XSBuiltIns…...

kubernetes实战与源码学习

1.1 关于Kubernetes的介绍与核心对象概念 关于Kubernetes的介绍与核心对象概念-阿里云开发者社区 k8s架构 核心对象 使用kubeadm10分钟部署k8集群 使用 KuboardSpray 安装kubernetes_v1.23.1 | Kuboard k8s-上部署第一个应用程序 Deployment基本概念 给应用添加service&a…...

CNCF x Alibaba云原生技术公开课 第八章 应用配置管理

Pod配置管理分类 可变配置就用 ConfigMap;敏感信息是用 Secret;身份认证是用 ServiceAccount;资源配置是用 Resources;安全管控是用 SecurityContext;前置校验是用 InitContainers。 1、ConfigMap 概念:…...

YUV实践记录

文章目录YUV基础介绍:不同采样YUV格式的区别为什么要使用YUV格式呢?YUV的存储方式Android中的YUV_420_888附录:YUV基础介绍: YUV在做手机图像或者视频处理的时候会经常用到的一个格式,用此文来记录YUV相关介绍&#xf…...

【题解】百度2020校招Web前端工程师笔试卷(第一批):单选题、多选题

题目来源 若有错误请指正! 单选 1 分页存储管理将进程的逻辑地址空间分成若干个页,并为各页加以编号,从0开始,若某一计算机主存按字节编址,逻辑地址和物理地址都是32位,页表项大小为4字节,若…...

探索云原生技术之容器编排引擎-kubeadm安装kubernetes1.21.10(新版:针对高版本内核)

❤️作者简介:2022新星计划第三季云原生与云计算赛道Top5🏅、华为云享专家🏅、云原生领域潜力新星🏅 💛博客首页:C站个人主页🌞 💗作者目的:如有错误请指正,将…...

2023广西自治区职业技能大赛“网络安全” 项目比赛任务书

2023广西自治区职业技能大赛“网络安全” 项目比赛任务书2023广西自治区职业技能大赛“网络安全” 项目比赛任务书A模块基础设施设置/安全加固(200分)A-1:登录安全加固(Windows, Linux)A-2:Nginx安全策略&a…...

Reactor模式

Reactor是一种设计模式,可以用于构建高并发的网络服务器。 Reactor模式的好处在于:可以在一个或多个reactor线程使用多路复用技术去管理所有网络连接连接建立、IO请求,保证工作线程不被IO阻塞。 前置知识:IO多路复用技术 1. 传统网…...

Git图解-IDEA中的Git操作

目录 一、配置Idea 二、项目克隆 三、文件状态识别 四、Git操作 4.1 git add--添加暂存区 4.2 git commit--提交本地仓库 4.3 git push--推送远程仓库 4.4 git pull--更新本地仓库 五、完整开发流程 5.1 步骤1:克隆项目 5.2 步骤2:创建自己开发…...

在一个web应用中应该如何完成资源的跳转

在一个web应用中通过两种方式,可以完成资源的跳转: 第一种方式:请求转发 第二种方式:重定向 转发和重定向的区别: 代码上的区别: 请求转发 // 获取请求转发器对象 RequestDispatcher dispatcher request.…...

前缀和部分题目

前缀和 前缀和指数组的前 N项之和,是个比较基础的算法 例题 面试题 17.05. 字母与数字 给定一个放有字母和数字的数组,找到最长的子数组,且包含的字母和数字的个数相同。 返回该子数组,若存在多个最长子数组,返回左…...

三天吃透MySQL面试八股文

本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~ Github地址:https://github.com/…...

Giving You A guide to learning any topic faster than 95% of people

A guide to learning any topic faster than 95% of people: Richard Feynman was a physician who won the Nobel Prize in 1965. But he became known for his great lectures. Why? He was able to explain complex concepts in simple terms with these 4 steps: 1 • E…...

(七十七)大白话MySQL是如何根据成本优化选择执行计划的?(中)

上次我们讲完了全表扫描的成本计算方法,相信大家应该都理解了,其实还是比较简单的,今天我们来讲一下索引的成本计算方法,因为除了全表扫描之外,还可能多个索引都可以使用,但是当然同时一般只能用一个索引&a…...

原来CSS 也可以节流啊

Ⅰ、前言 「节流」 是为了减少请求的触发频率,不让用户点的太快,达到节省资源的目的 ;通常 我们采用 JS 的 定时器 setTimeout ,来控制点击多少秒才能在触发;其实 通过 CSS 也能达到 「节流」 的目的,下面…...

UE官方教程笔记03-功能、术语、操作简介

对官方教程视频[官方培训]03-UE功能、术语、操作简介 | 徐良安 Epic的笔记这一部分基本都是走马观花的简单介绍功能世界创建建模Mesh editingtool是一个全新的建模工具,具备大多数的主流建模软件的核心功能HOUDINI ENGINE FOR UNREALHoudini编辑器,可以用…...

BN,LN,IN,GN的理解和用法

绿色区域表示将该区域作用域(四种方法都贯穿了w,h维度),即将该区域数值进行归一化,变为均值为0,标准差为1。BN的作用区域时N,W,H,表示一个batch数据的每一个通道均值为0,标准差为1;LN则是让每个数据的所有channel的均值…...

Linux:epoll模式web服务器代码,代码debug

源码&#xff1a; https://blog.csdn.net/weixin_44718794/article/details/107206136 修改的地方&#xff1a; 修改后代码&#xff1a; #include <stdio.h> #include <unistd.h> #include <stdlib.h> //#include “epoll_server.h” #ifndef _EPOLL_SER…...

SpringSecurity学习(四)密码加密、RememberMe记住我

文章目录密码加密一、简介密码为什么要加密常见的加密解决方案PasswordEncoder详解DelegatingPasswordEncoder二、自定义加密方式1. 使用灵活的密码加密方案&#xff08;BCryptPasswordEncoder&#xff09;加密验证&#xff08;推荐&#xff09;需要在密码前指定加密类型{bcryp…...

【Java学习笔记】Arrays类

Arrays 类 1. 导入包&#xff1a;import java.util.Arrays 2. 常用方法一览表 方法描述Arrays.toString()返回数组的字符串形式Arrays.sort()排序&#xff08;自然排序和定制排序&#xff09;Arrays.binarySearch()通过二分搜索法进行查找&#xff08;前提&#xff1a;数组是…...

深入理解JavaScript设计模式之单例模式

目录 什么是单例模式为什么需要单例模式常见应用场景包括 单例模式实现透明单例模式实现不透明单例模式用代理实现单例模式javaScript中的单例模式使用命名空间使用闭包封装私有变量 惰性单例通用的惰性单例 结语 什么是单例模式 单例模式&#xff08;Singleton Pattern&#…...

Neo4j 集群管理:原理、技术与最佳实践深度解析

Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...

令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍

文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结&#xff1a; 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析&#xff1a; 实际业务去理解体会统一注…...

C# SqlSugar:依赖注入与仓储模式实践

C# SqlSugar&#xff1a;依赖注入与仓储模式实践 在 C# 的应用开发中&#xff0c;数据库操作是必不可少的环节。为了让数据访问层更加简洁、高效且易于维护&#xff0c;许多开发者会选择成熟的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;SqlSugar 就是其中备受…...

CMake控制VS2022项目文件分组

我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

二维FDTD算法仿真

二维FDTD算法仿真&#xff0c;并带完全匹配层&#xff0c;输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...

02.运算符

目录 什么是运算符 算术运算符 1.基本四则运算符 2.增量运算符 3.自增/自减运算符 关系运算符 逻辑运算符 &&&#xff1a;逻辑与 ||&#xff1a;逻辑或 &#xff01;&#xff1a;逻辑非 短路求值 位运算符 按位与&&#xff1a; 按位或 | 按位取反~ …...

跨平台商品数据接口的标准化与规范化发展路径:淘宝京东拼多多的最新实践

在电商行业蓬勃发展的当下&#xff0c;多平台运营已成为众多商家的必然选择。然而&#xff0c;不同电商平台在商品数据接口方面存在差异&#xff0c;导致商家在跨平台运营时面临诸多挑战&#xff0c;如数据对接困难、运营效率低下、用户体验不一致等。跨平台商品数据接口的标准…...

Python第七周作业

Python第七周作业 文章目录 Python第七周作业 1.使用open以只读模式打开文件data.txt&#xff0c;并逐行打印内容 2.使用pathlib模块获取当前脚本的绝对路径&#xff0c;并创建logs目录&#xff08;若不存在&#xff09; 3.递归遍历目录data&#xff0c;输出所有.csv文件的路径…...