json.stringify遇循环引用必报错,因其深度优先遍历无状态记忆机制,无法识别重复对象;而json标准本身不支持循环结构,故引擎主动熔断抛出typeerror。

JSON.stringify 一遇到循环引用就报错,根本原因在于它内部没有“记忆机制”——它不记录自己已经遍历过哪些对象。
深度优先遍历 + 无状态检查 = 必然崩溃
JSON.stringify 按照深度优先方式递归访问每个属性值。当碰到一个对象时,它会继续进入其所有可枚举属性;如果某个属性又指向之前见过的对象(比如父节点、自身或中间某层对象),它无法识别这是“重复访问”,而是当作新对象继续钻进去。
这种无状态的遍历最终导致无限递归,调用栈迅速耗尽,JavaScript 引擎主动抛出 TypeError: Converting circular structure to JSON 来终止执行,防止进程卡死。
JSON 格式本身就不支持循环结构
标准 JSON 是一种纯数据交换格式,设计上只描述树状结构,不包含指针、引用或 ID 关联等元信息。它没有类似 $ref 这样的语法来表达“此处引用前面第 3 个对象”。所以即使引擎想绕过错误,也无法在字符串中合法表达循环关系。
换句话说:不是实现偷懒,而是规范不允许。JSON 的语义决定了它天然排斥循环。
为什么 WeakMap 能解决,而普通 Map 不行?
真正能安全处理循环引用的深拷贝或序列化方案(如 flatted 或手写 deepClone),都依赖 WeakMap 来做“已访问对象登记”。它的关键特性是:
- 键必须是对象,且不会阻止该对象被垃圾回收
- 可以精确判断“这个原始对象我是否处理过”,从而跳过重复递归
- 避免内存泄漏——临时缓存随原始对象释放而自动清理
换成普通 Map,虽然也能记,但容易因强引用导致对象无法释放,尤其在长期运行的前端应用中会悄悄积累内存压力。
浏览器原生 structuredClone 是个例外
现代浏览器提供的 structuredClone() 可以直接处理循环引用,因为它底层使用的是 V8 的克隆算法,自带对象图遍历与引用映射表,本质上和 flatted 的思路一致——只是封装得更底层、更透明。但它不生成 JSON 字符串,也不支持函数、undefined 等非序列化值,适用场景有限。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











