写点什么

JavaScript 之 structuredClone 现代深拷贝

作者:南城FE
  • 2024-03-04
    广东
  • 本文字数:1793 字

    阅读完需:约 6 分钟

在 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 类型,如DateSetMapErrorRegExpArrayBufferBlobFileImageData


哪些不能拷贝:


  • 函数

  • DOM 节点

  • 属性描述、settergetter

  • 对象原型链


所支持的完整列表:


ArrayArrayBufferBooleanDataViewDateError类型(下面具体列出的类型)、MapObject,但仅限于普通对象、原始类型,除了symbol(又名numberstringnullundefinedbooleanBigInt)、RegExpSetTypedArray


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能力实现深度拷贝对象。每种方式都有其优缺点,具体使用方式取决于你的需求和目标对象的类型。

参考

  • Deep Cloning Objects in JavaScript, the Modern Way(www.builder.io/blog/structured-clone)

  • mozilla structuredClone(developer.mozilla.org/zh-CN/docs/Web/API/structuredClone)




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


专注前端开发,分享前端相关技术干货,公众号:南城大前端(ID: nanchengfe)

发布于: 刚刚阅读数: 3
用户头像

南城FE

关注

公众号@南城大前端 2019-02-12 加入

专注前端开发,分享前端知识

评论

发布
暂无评论
JavaScript之structuredClone现代深拷贝_JavaScript_南城FE_InfoQ写作社区