深入探索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
能力实现深度拷贝对象。每种方式都有其优缺点,具体使用方式取决于你的需求和目标对象的类型。
更多内容请看:https://mybj123.com/20631.html