fs.writeFileSync未将所有变量写入JSON文件的问题排查
问题分析:JSON.stringify输出截断,缺少inturn/outturn属性
问题重现
你编写的Node.js代码如下:
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:resume,inturn:fighter1,outturn:fighter2}; fs.writeFileSync(file,JSON.stringify(all,circular()),{encoding:'utf8',flag:'w'});
预期写入文件的JSON包含gameon、fighter1、fighter2、resume、inturn、outturn所有属性,但实际输出仅到resume就截断,缺少inturn和outturn属性。
原因解析
问题出在你写的circular替换函数上:
- 函数内部用
WeakSet记录了所有已经处理过的对象,而非仅跟踪当前遍历链中的循环引用。 - 当
JSON.stringify处理all对象时:- 处理
fighter1和fighter2时,这两个对象会被加入seen集合。 - 后续处理
inturn(值为fighter1)和outturn(值为fighter2)时,因为seen中已经存在这两个对象,替换函数会返回undefined。 - 而
JSON.stringify的规则是:如果替换函数返回undefined,对应的属性会被直接忽略,导致这两个属性从输出中消失。
- 处理
简单说,你的代码误把重复引用(同一个对象被多个属性引用)当成循环引用(对象引用自身或形成遍历闭环)处理了,从而错误过滤了正常的重复引用属性。
修复方案
修改替换函数,只过滤真正的循环引用(即当前遍历栈中已存在的对象),而非所有见过的对象。可以用栈来跟踪当前正在遍历的对象:
const circularReplacer = () => { const traversalStack = []; return (key, value) => { if (typeof value === 'object' && value !== null) { // 检查当前对象是否在遍历栈中,判断是否为循环引用 const stackIndex = traversalStack.indexOf(value); if (stackIndex !== -1) { // 遇到循环引用,返回undefined跳过 return; } // 将当前对象加入遍历栈 traversalStack.push(value); // 利用异步时机在属性处理完成后弹出栈 process.nextTick(() => traversalStack.pop()); } return value; }; }; // 使用修复后的替换函数 fs.writeFileSync(file, JSON.stringify(all, circularReplacer()), { encoding: 'utf8', flag: 'w' });
另外建议给所有变量加上const/let声明,避免全局变量污染:
const gameon = 1; const fighter1 = {"userid":"97","username":"john","items":{},"ailments":{}}; const fighter2 = {"userid":"91","username":"james","items":{},"ailments":{}}; const resume = 30; const all = {gameon,fighter1,fighter2,resume,inturn:fighter1,outturn:fighter2};
补充说明
如果你的场景中不存在真正的循环引用(比如fighter1.inturn = fighter1这种情况),其实可以直接去掉替换函数,JSON.stringify本身就能正常处理重复引用,不会报错。只有当存在循环引用时,才需要替换函数来避免Converting circular structure to JSON错误。
内容的提问来源于stack exchange,提问作者Cain Nuke
相关产品推荐
相关产品推荐

