循环引用隔离的核心是对象身份识别与引用关系映射:structuredclone隐式用图遍历+弱引用机制处理,手写方案显式用weakmap缓存原对象到克隆体的映射,确保新旧对象零共享且不阻塞gc。

循环引用隔离的核心,是避免重复处理同一对象导致无限递归或数据错乱。structuredClone 和手写 WeakMap 方案看似路径不同,但底层都依赖“对象身份识别 + 引用关系映射”这一共性逻辑。
对象身份识别:不是看值,而是看“是不是同一个内存地址”
两者都不靠 JSON.stringify 或属性遍历来判断是否重复——那会漏判、误判。它们真正依赖的是 JavaScript 的原始引用语义:
- structuredClone 内部用类似 WeakMap 的机制隐式记录已访问对象(虽然不暴露给开发者),靠对象引用本身做键;
- 手写方案显式用 WeakMap
,利用 WeakMap 键的弱引用特性,既可精准匹配原对象,又不会阻止垃圾回收。
引用关系映射:重建结构,而非复制路径
遇到 obj.a = obj 这类自引用时,关键不是“跳过”,而是“记下来、后面补上”:
- structuredClone 采用图遍历(graph traversal),先扫描所有节点建立引用拓扑,再按图结构重建新对象,并把
clone.a指向新生成的clone自身; - WeakMap 手写方案在首次进入
obj时就存下map.set(obj, clone),后续递归中一旦再见到obj,立刻返回缓存的clone,从而让clone.a === clone成立。
隔离的本质:新旧对象之间零共享
所谓“隔离”,是指克隆后的新对象图完全独立于原始对象图:
- structuredClone 对每个引用类型都创建全新实例(新 Date、新 Map、新 Uint8Array),连 ArrayBuffer 都支持 transfer 转移,确保底层内存不共用;
- WeakMap 方案虽不自动处理 TypedArray 或 RegExp,但只要在递归分支里对每种类型做对应构造(如
new Uint8Array(original)),也能达成同样效果; - 二者都不拷贝函数、Symbol、原型链、DOM 节点——这些本就不该被“隔离”,而是被明确排除,避免语义污染。
为什么 WeakMap 是手写方案的最优选择
不是因为它“高级”,而是它恰好匹配这个场景的技术约束:
- 键必须是对象且不可枚举——WeakMap 天然满足,普通 Map 用对象作键会引发内存泄漏;
- 不需要长期持有原对象——WeakMap 的弱引用特性,让原始对象被回收后,缓存条目自动失效;
- 不参与序列化——它只在克隆过程临时存在,不影响输出结果,也不被 structuredClone 自身处理(因为 structuredClone 本来就不克隆 WeakMap)。











