内容提要
structuredClone() 是 JavaScript 内置的深拷贝 API,基于结构化克隆算法,支持循环引用、Date、Set、Map、Error、ArrayBuffer 等类型,并可通过 transfer 转移对象所有权。它不能克隆函数和 DOM 节点,也不保留 RegExp 的 lastIndex、属性描述符及原型链属性。Node.js 从 v17 起支持,低版本浏览器需 polyfill。相比 JSON 方法和 lodash,它是更优的深拷贝方案,但需注意兼容性。
延伸解读
结构化克隆算法的限制
structuredClone 基于结构化克隆算法,但该算法有明确限制:无法克隆 Function 对象和 DOM 节点,尝试克隆会抛出 DATA_CLONE_ERR 异常。此外,RegExp 的 lastIndex 字段、属性描述符(如 setters/getters)以及原型链上的属性都不会被保留。这意味着如果对象依赖这些特性,深拷贝后可能行为不一致,使用时需留意。
transfer 参数的使用与兼容性风险
structuredClone 支持通过 transfer 参数转移可转移对象(如 ArrayBuffer)的所有权,转移后原始对象将无法访问。但 core-js 的 polyfill 尚未解决 ArrayBuffer 实例和许多平台类型在大多数引擎中传输的问题。因此,在需要兼容低版本浏览器时,应尽量避免使用第二个参数,以免出现意外行为。
与其他深拷贝方法的对比
相比 JSON.parse(JSON.stringify()),structuredClone 能正确处理 Date、Set、Map 等复杂类型,而 JSON 方法会将 Date 转为字符串。相比 lodash 的 _.cloneDeep(),structuredClone 是全局 API,无需引入额外库,但 lodash 支持更复杂的数据类型。structuredClone 的主要缺点是兼容性问题,低版本浏览器和 Node.js v17 以下需 polyfill。
兼容性与 polyfill 注意事项
Node.js 从 v17.0 起支持 structuredClone,低版本浏览器可能需要 polyfill。core-js 提供了功能完整的 polyfill,它会检查原生实现是否正确,若不正确则使用递归遍历实现深拷贝,并处理边界案例。但该 polyfill 对 transfer 参数的支持有限,因此在使用 transfer 时需谨慎评估环境兼容性。
Q&A
structuredClone 是什么?它有什么作用?
structuredClone() 是 JavaScript 内置的深拷贝 API,使用结构化克隆算法对给定值进行深拷贝。它支持循环引用,并可通过 transfer 参数转移对象所有权。
structuredClone 支持哪些数据类型?
structuredClone 支持拷贝多种内置类型,包括 Date、Set、Map、Error(部分类型)、RegExp、ArrayBuffer、Blob、File、ImageData 等,以及数组、简单对象和除 symbol 外的基本类型。
structuredClone 有哪些局限性?不能克隆什么?
structuredClone 不能克隆 Function 对象和 DOM 节点,尝试克隆会抛出 DATA_CLONE_ERR 异常。此外,它不保留 RegExp 的 lastIndex 字段、属性描述符(如 setters/getters)、以及原型链上的属性。
structuredClone 的兼容性如何?低版本环境怎么办?
Node.js 从 v17.0 起支持 structuredClone。低版本浏览器可能需要 polyfill,例如 core-js 或 ungap/structured-clone 提供的实现。注意 core-js 的 polyfill 尚未解决 ArrayBuffer 实例和许多平台类型的 transfer 问题,兼容低版本浏览器时应避免使用 transfer 参数。
structuredClone 和 JSON.parse(JSON.stringify()) 以及 lodash 的 cloneDeep 相比有什么优缺点?
structuredClone 是全局 API,简单易用,支持复杂数据类型;JSON 方法只能处理基本对象、数组和原始类型,其他类型处理不可预测(如 Date 转为字符串);lodash 的 cloneDeep 支持复杂类型但体积较大(约 17.4 KB,gzip 后 5.3 KB)。structuredClone 的主要缺点是可能有兼容性问题。
structuredClone 的 transfer 参数是做什么的?
transfer 是一个可转移对象的数组,这些对象不会被克隆,而是将所有权转移到新对象上。转移后,原始对象将无法再访问这些对象。