structuredClone
structuredClone 是 JavaScript 全局对象上的一个方法,用于创建对象的深拷贝。它基于浏览器实现的结构化克隆算法(Structured Clone Algorithm),该算法最初设计用于在 Web Worker 之间传递结构化数据。structuredClone 能够递归地克隆对象的所有可枚举属性,包括嵌套结构,并且能够正确处理循环引用,是目前 JavaScript 中最强大的原生深拷贝方案。
调用方式非常简单:structuredClone(value) 即可返回一个与原始值完全独立的深拷贝副本。它不修改原始对象,也不会共享任何引用关系。该方法在现代浏览器和 Node.js 17.0+ 中均已原生支持,无需引入第三方库。
深拷贝(Deep Clone)
深拷贝是指创建一个与原始对象完全独立的新对象,新对象中的所有属性值都是原始对象对应属性值的副本,而非引用。这意味着对新对象或原始对象中任何层级的属性进行修改,都不会影响到对方。深拷贝与浅拷贝(Shallow Clone)相对,浅拷贝只复制对象的第一层属性,嵌套的引用类型属性仍然共享同一份数据。
在 JavaScript 中,实现深拷贝的常见方式包括:JSON.parse(JSON.stringify())、structuredClone()、第三方库的 cloneDeep 方法,以及手写的递归克隆函数。每种方案都有各自的优缺点,structuredClone 在类型支持和性能方面具有明显优势。
结构化克隆算法
结构化克隆算法是浏览器内部实现的一套对象复制规则,用于在不同执行上下文之间传递数据。该算法能够识别并正确复制大多数 JavaScript 内置类型,包括普通对象、数组、Date、RegExp、Map、Set、ArrayBuffer、TypedArray、Blob、File、ImageData、DataView 等。它还能够处理对象之间的循环引用关系。
与 JSON 序列化不同,结构化克隆算法直接在内存层面操作对象图,不需要先转换为字符串再解析回来,因此不会丢失类型信息。例如,Date 对象克隆后仍然是 Date 对象,而不是字符串;RegExp 克隆后仍然是 RegExp 对象,而不是空对象。这是 structuredClone 方法的核心技术优势。
循环引用(Circular Reference)
循环引用是指对象之间通过属性直接或间接引用自身形成的结构。例如对象 A 的某个属性指向对象 B,而对象 B 的某个属性又指回对象 A,形成一个引用环。传统的 JSON.parse(JSON.stringify()) 方法在遇到循环引用时会抛出 TypeError 错误,因为 JSON 格式无法表达循环结构。
structuredClone 通过内部维护一个已访问对象的映射表来处理循环引用。当克隆过程中遇到一个已经被克隆过的对象引用时,它会直接复用之前创建的克隆副本,而不是重新克隆,从而避免了无限递归。这使得 structuredClone 能够完美地保留原始对象的循环引用结构。
Transfer(转移)
Transfer 是 structuredClone 的第二个参数选项,用于指定需要"转移"而非"复制"的对象列表。目前主要支持 ArrayBuffer、MessagePort、ReadableStream、WritableStream、TransformStream 和 WebAssembly.Memory 等可转移对象(Transferable Objects)。
转移与复制的本质区别在于:复制操作会创建一份新的副本,原始数据保持不变;而转移操作则是将数据的所有权从原始对象转移到克隆对象,转移后原始对象中的可转移数据会被清空(例如 ArrayBuffer 的字节长度变为 0),克隆对象获得完整的数据。这种方式在处理大型二进制数据时可以避免数据复制,显著提升性能。
ArrayBuffer
ArrayBuffer 是 JavaScript 中用于表示固定长度的二进制数据缓冲区的对象。它是一块连续的内存区域,不能直接读写其中的数据,需要通过 TypedArray(如 Uint8Array、Int32Array 等)或 DataView 对象来访问。ArrayBuffer 广泛应用于图像处理、音频视频处理、网络通信和文件操作等需要处理二进制数据的场景。
structuredClone 支持对 ArrayBuffer 进行克隆,克隆后的 ArrayBuffer 拥有与原始数据完全相同的内容,但存储在独立的内存区域中。当配合 Transfer 参数使用时,ArrayBuffer 的所有权会被转移,原始对象中的 buffer 将被清空,从而避免了大块内存的复制开销。
Map 与 Set
Map 是 JavaScript ES6 引入的键值对集合数据结构,与普通对象不同,Map 的键可以是任意类型的值(包括对象、函数和基本类型),并且 Map 保持了键值对的插入顺序。Set 是 ES6 引入的唯一值集合数据结构,用于存储不重复的值,同样保持插入顺序。
structuredClone 能够正确克隆 Map 和 Set 对象,克隆后的 Map 保持相同的键值对映射关系,克隆后的 Set 保持相同的成员列表。使用传统的 JSON 方法进行序列化时,Map 和 Set 会被转换为空对象 {},丢失所有数据。这是 structuredClone 相比 JSON 方法的重要优势之一。
TypedArray(类型化数组)
TypedArray 是一组用于表示固定长度的二进制数据缓冲区的视图对象的统称,包括 Int8Array、Uint8Array、Uint8ClampedArray、Int16Array、Uint16Array、Int32Array、Uint32Array、Float32Array、Float64Array、BigInt64Array 和 BigUint64Array 等。TypedArray 提供了对 ArrayBuffer 中数据的类型化访问方式。
structuredClone 支持对 TypedArray 进行完整克隆,包括其底层的 ArrayBuffer 数据。克隆后的 TypedArray 拥有独立的内存空间,修改其中一个不会影响另一个。这对于处理图像像素数据、音频采样数据等二进制场景中的深拷贝需求非常重要。
UD5工具箱