structuredclone() 原生支持循环引用及 date、map、set、arraybuffer、bigint、regexp 等 json 无法处理的类型,自动记录已访问对象以复现引用关系,但不支持函数、undefined、symbol、error、proxy、weakmap/weakset 和 dom 节点。

直接用 structuredClone() 就行——它原生支持循环引用,无需额外标记、追踪或打断,拷贝后引用关系完全复现,且性能优于 JSON 方案和手写递归。
为什么它天然“感知”循环引用
structuredClone 使用浏览器/运行时内置的结构化克隆算法,在遍历过程中自动记录已访问对象的引用标识。当再次遇到同一对象(比如 obj.self = obj),它不会重复克隆,而是复用已生成的副本并重建指向关系。
- 结果中
cloned.self === cloned为 true,和原始对象行为一致 - 深层嵌套中的循环(如
obj.nested.ref = obj)同样被精准还原 - 整个过程对开发者完全透明,不需手动维护 visited Set 或 ID 映射表
它能拷贝哪些 JSON 处理不了的关键类型
除了循环引用,structuredClone 还原生支持多种 JSON 无法保留结构或直接报错的类型:
- Date:保持 Date 实例,不是 ISO 字符串
- Map / Set:克隆后仍是 Map/Set,键值对完整,非空对象 {}
-
ArrayBuffer / TypedArray:二进制数据零拷贝转移(配合
transfer选项可进一步优化) - BigInt:保留数值精度,JSON 会直接报错
- RegExp:部分浏览器支持(Chrome/Firefox 稳定,Safari 需 ≥15.4)
必须注意的限制与兜底策略
它不是万能的,遇到不支持类型会抛 DataCloneError,且不兼容旧环境:
-
不支持:函数、
undefined、Symbol(除Symbol.for)、Error 实例(stack/cause 丢失)、Proxy、WeakMap/WeakSet、DOM 节点 - 兼容性检测不能只看是否存在:Safari 15.2 声称支持但 BigInt 拷贝失败,建议加探测
-
降级要谨慎:若业务确定含循环引用,fallback 到
JSON.parse(JSON.stringify())会立即报错,此时应强制升级环境或引入lodash.cloneDeep
推荐的安全调用模式
兼顾健壮性与简洁性,可封装如下:
function safeStructuredClone(obj) {
if (typeof structuredClone !== 'function') {
throw new Error('structuredClone not supported');
}
try {
return structuredClone(obj);
} catch (err) {
if (err.name === 'DataCloneError') {
throw new Error(`Cloning failed: ${err.message}`);
}
throw err;
}
}
该模式不隐藏错误,明确暴露不支持的类型,便于定位问题根源,也避免静默丢弃关键字段。











