Node.js 如何打印循环引用的对象(JSON 格式)?

JSON.stringify 遇到循环引用直接报错。解决方案:用 util.inspect 打印(自动处理循环引用),或给 JSON.stringify 传 replacer 函数剔除循环引用。本文给出三种写法。

最佳实践
Node.js 事件循环与异步任务示意

最简单:用 util.inspect(),天生支持循环引用。JSON.stringify 遇到循环引用会直接抛 TypeError: Converting circular structure to JSON。

方法一:util.inspect(推荐)

const util = require('util');

const obj = {
  prop1: 'value1',
  prop2: { prop3: 'value3' }
};
obj.circularRef = obj;   // 制造循环引用

console.log(util.inspect(obj, { depth: null }));
// 输出: { prop1: 'value1', prop2: { prop3: 'value3' }, circularRef: <ref *1> [Circular] }

depth: null 取消默认的嵌套层级限制(默认只展开 2 层),循环引用处显示 <ref *1> [Circular]。

其实直接 console.log(obj) 内部也走 util.inspect,同样能打印循环结构——只是层级深了会被折叠。

方法二:JSON.stringify + replacer

需要真正的 JSON 字符串(比如要写文件)时,用 replacer 函数剔除循环引用:

function makeReplacer() {
  const ancestors = [];
  return function (key, value) {
    if (typeof value !== 'object' || value === null) return value;
    while (ancestors.length && ancestors[ancestors.length - 1] !== this) ancestors.pop();
    if (ancestors.includes(value)) return '[Circular]';
    ancestors.push(value);
    return value;
  };
}
const json = JSON.stringify(obj, makeReplacer(), 2);

console.log(json);

方法三:现成库

safe-stable-stringify、flatted 等库有不同的循环引用与类型策略,不能统一保证 BigInt、Map 或无损还原,项目里高频遇到循环引用(如打印 ORM 实体)时直接用库。

常见问题(FAQ)

Q:循环引用从哪来的?
常见于:ORM 实体互相关联(User.posts → Post.author → User)、DOM-like 树结构(parent/children)、Express 的 req/res 对象。

Q:util.inspect 的输出能再 JSON.parse 回来吗?
不能,它是给人看的调试格式,不是合法 JSON。方法二输出合法 JSON,但占位符丢失循环关系;flatted 需配套 parse 才能恢复其支持的引用结构,不可与 JSON.parse 任意混用。

Q:为什么打印 express 的 req 会炸?
req 内部有大量循环引用(req.res.req...)。别整个打印,选择 method、脱敏后的路径和必要白名单字段;URL 查询及 Authorization/Cookie 等头可能含密钥,不应整包输出。

参考资料

获取专属方案

联系我们

加入社区

微信扫码
加入官方交流群

立即体验

在线开通,按量计费,真正的云服务!

立即开始

选择观测云版本

代码托管平台