自定义JSON循环结构处理函数导致输出截断的原因解析
JSON.stringify循环引用处理函数导致输出截断的原因及修复
问题重现
为解决JSON转换循环结构报错,编写了以下处理函数:
circular = () => { //fix circular stuff for json.stringify seen = new WeakSet(); return (key, value) => { if (typeof value === 'object' && value !== null) { if (seen.has(value)) { return; } seen.add(value); } return value; }; };
在以下场景中出现输出"截断"问题:
var gameon = 1; var fighter1 = {"userid":"97","username":"john","items":{},"ailments":{}}; var fighter2 = {"userid":"91","username":"james","items":{},"ailments":{}}; var resume = 30; all = {"gameon":gameon,"fighter1":fighter1,"fighter2":fighter2,"resume":30,"inturn":fighter1,"outturn":fighter2}; console.log(JSON.stringify(all,circular()));
执行后输出仅到resume字段就结束,后续的inturn和outturn字段丢失:
{"gameon":1,"fighter1":{"userid":"97","username":"john","items":{},"ailments":{}},"fighter2":{"userid":"91","username":"james","items":{},"ailments":{}},"resume":30}
(注:你提供的输出末尾多了一个多余引号,应为输入失误)
原因分析
重复引用返回
undefined导致键值对被忽略:
当JSON.stringify遍历到inturn: fighter1时,fighter1对象已经在处理fighter1字段时被加入了seenWeakSet,此时处理函数返回undefined。而JSON.stringify的规则是:如果replacer函数返回undefined,对应的键值对会被直接跳过,不会出现在最终的JSON字符串中。同理outturn: fighter2也会被跳过,看起来像是输出被"截断"。全局变量
seen的潜在污染:
函数中的seen没有用let/const声明,属于隐式全局变量。如果多次调用circular(),会导致seen被重复覆盖,可能引发其他场景下的异常。
修复方案
修改处理函数,针对重复引用返回可序列化的标识,同时将seen改为闭包局部变量:
const circular = () => { const seen = new WeakSet(); // 改为局部变量,避免全局污染 return (key, value) => { if (typeof value === 'object' && value !== null) { if (seen.has(value)) { // 返回自定义标识,而非undefined,保留键值对 return `[Circular Reference: ${value.userid || key}]`; // 也可以直接返回对象的唯一标识,比如value.userid // return value.userid; } seen.add(value); } return value; }; };
修改后,再执行原测试代码,输出会保留inturn和outturn字段,显示为自定义的循环引用标识,不会出现"截断":
{"gameon":1,"fighter1":{"userid":"97","username":"john","items":{},"ailments":{}},"fighter2":{"userid":"91","username":"james","items":{},"ailments":{}},"resume":30,"inturn":"[Circular Reference: 97]","outturn":"[Circular Reference: 91]"}
内容的提问来源于stack exchange,提问作者Cain Nuke
相关产品推荐
相关产品推荐

