json.stringify处理循环引用必报错,因json标准不支持;可用weakmap+replacer跳过、structuredclone解环或flatted库实现可还原序列化。

直接用 JSON.stringify 处理含循环引用的对象一定会报错:TypeError: Converting circular structure to JSON。这不是 bug,而是 JSON 标准本身不支持对象间相互引用。避免报错的关键不是“绕过去”,而是提前干预序列化过程——要么跳过循环部分,要么先断开循环再序列化。
用 replacer 函数拦截并跳过重复对象
这是最轻量、兼容性最好的方案,适合大多数前端场景。核心是用 WeakMap 记录已访问的对象引用,遇到重复就返回 undefined(该字段会被忽略)或占位字符串(如 "[Circular]"):
- 定义一个
WeakMap存储已遍历的对象,避免内存泄漏 - 在
replacer函数中判断当前值是否已存在 map 中;若存在,返回undefined或自定义标记 - 注意:该函数对数组索引(如
"0")、对象属性一视同仁,无需额外区分
示例代码:
const replacer = (key, value) => {
if (typeof value === 'object' && value !== null) {
if (seen.has(value)) return '[Circular]';
seen.set(value, true);
}
return value;
};
JSON.stringify(obj, replacer);
用 structuredClone 先解环再序列化
现代环境(Chrome 98+ / Firefox 94+ / Node.js 17.0+)推荐这条路。structuredClone() 原生支持循环引用、Map、Set、Date 等,它会生成一个无环的新对象,之后再 JSON.stringify 就不会出错:
- 调用
structuredClone(obj)得到深拷贝,原始循环已被“展开”为独立副本 - 接着
JSON.stringify(structuredClone(obj))即可安全输出 - 注意:
structuredClone不处理function、undefined、BigInt、Promise,这些本就不属于 JSON 合法类型,需提前清理或替换
引入 flatted 库实现可还原的序列化
如果业务需要反序列化后**完全还原原始引用关系**(比如调试图结构、保存状态快照),那就不能只跳过或展开,而要用支持引用协议的方案。flatted 是目前最轻量(仅 0.5KB)、API 兼容原生 JSON 的选择:
- 安装:
npm install flatted - 用法与原生一致:
import { stringify, parse } from 'flatted' - 它把循环对象转成带
{"#": 1}这类索引标记的 JSON 字符串,反序列化时自动重建引用 - 输出仍是标准 JSON 字符串格式,可直接传输,但必须用
flatted.parse()才能还原循环
开发阶段快速定位循环在哪
报错时别急着写 replacer,先确认问题源头:
- 浏览器里直接
console.dir(obj, { depth: null }),控制台会标出[Circular]并支持点击展开 - 或者用简单检测函数递归遍历,配合
WeakMap记录路径,首次命中重复对象时打印栈信息 - 如果
structuredClone(obj)也报错,说明对象里混入了 DOM 节点、window、MobX observable 等不可克隆类型,需提前过滤
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











