如何实现JS对象树形格式化工具?递归代码问题求助
树形结构格式化工具代码修正及JS对象数组学习资源
问题背景
需要实现一个格式化工具,将结构为{ key: '', value: '', type: '' }的对象数组,转换为指定的树形输出格式。原递归函数stylish存在以下问题:
- 简单值(如
false、200)显示为空对象{} - 对象类型值显示为
[object Object] - 缩进层级混乱
- 输出格式与目标不符
输入数据(INPUT)
const el = [ { "key": "common", "value": [ { "key": "follow", "value": false, "type": "added" }, { "key": "setting1", "value1": "Value 1", "value2": "Value 1", "type": "nochanged" }, { "key": "setting2", "value": 200, "type": "deleted" }, { "key": "setting3", "value1": true, "value2": null, "type": "changed" }, { "key": "setting4", "value": "blah blah", "type": "added" }, { "key": "setting5", "value": { "key5": "value5" }, "type": "added" }, { "key": "setting6", "value": [ { "key": "doge", "value": [ { "key": "wow", "value1": "", "value2": "so much", "type": "changed" } ], "type": "object" }, { "key": "key", "value1": "value", "value2": "value", "type": "nochanged" }, { "key": "ops", "value": "vops", "type": "added" } ], "type": "object" } ], "type": "object" }, { "key": "group1", "value": [ { "key": "baz", "value1": "bas", "value2": "bars", "type": "changed" }, { "key": "foo", "value1": "bar", "value2": "bar", "type": "nochanged" }, { "key": "nest", "value1": { "key": "value" }, "value2": "str", "type": "changed" } ], "type": "object" }, { "key": "group2", "value": { "abc": 12345, "deep": { "id": 45 } }, "type": "deleted" }, { "key": "group3", "value": { "deep": { "id": { "number": 45 } }, "fee": 100500 }, "type": "added" } ]
目标输出(OUTPUT)
{ common: { + follow: false setting1: Value 1 - setting2: 200 - setting3: true + setting3: null + setting4: blah blah + setting5: { key5: value5 } setting6: { doge: { - wow: + wow: so much } key: value + ops: vops } } group1: { - baz: bas + baz: bars foo: bar - nest: { key: value } + nest: str } - group2: { abc: 12345 deep: { id: 45 } } + group3: { deep: { id: { number: 45 } } fee: 100500 } }
原错误代码
const stylish = (data, replacer = ' ', spacesCount = 1) => { const iter = (data, depth) => { const arr = []; const keys = Object.keys(data); const sizeBefore = depth * spacesCount; const replacerBefore = replacer.repeat(sizeBefore); keys.forEach((key) => { if (data[key].type === 'object') { arr.push(`${replacerBefore} ${data[key].key} : ${iter(data[key].value)}`); } if (data[key].type === 'added') { arr.push(`${replacerBefore}+ ${data[key].key} : ${iter(data[key].value)}`); } if (data[key].type === 'deleted') { arr.push(`${replacerBefore}- ${data[key].key} : ${iter(data[key].value)}`); } if (data[key].type === 'changed') { arr.push(`${replacerBefore}- ${data[key].key} : ${data[key].value1}`); arr.push(`${replacerBefore}+ ${data[key].key} : ${data[key].value2}`); } if (data[key].type === 'nochanged') { arr.push(`${replacerBefore} ${data[key].key} : ${data[key].value2}`); } }); return [ '{', ...arr, '}' ].join('\n'); };
原错误输出
{ common : { + follow : { } setting1 : Value 1 - setting2 : { } - setting3 : true + setting3 : null + setting4 : { } + setting5 : { } setting6 : { doge : { - wow : + wow : so much } key : value + ops : { } } } group1 : { - baz : bas + baz : bars foo : bar - nest : [object Object] + nest : str } - group2 : { } + group3 : { } }
修正后的代码
const stylish = (data, replacer = ' ', spacesCount = 4) => { // 格式化简单值,处理null、空字符串等 const formatValue = (value) => { if (value === null) return 'null'; if (typeof value === 'string' && value === '') return ''; if (typeof value === 'object' && !Array.isArray(value) && !('type' in value)) { return iter(value, 1); } return typeof value === 'string' ? value : String(value); }; const iter = (data, depth) => { const indent = replacer.repeat(depth * spacesCount); const innerIndent = replacer.repeat((depth + 1) * spacesCount); const lines = []; // 处理节点数组(最外层、object类型的value) if (Array.isArray(data)) { data.forEach(item => { const { key, type } = item; let valueStr = ''; switch (type) { case 'object': valueStr = iter(item.value, depth + 1); lines.push(`${indent} ${key}: ${valueStr}`); break; case 'added': valueStr = formatValue(item.value); lines.push(`${indent}+ ${key}: ${valueStr}`); break; case 'deleted': valueStr = formatValue(item.value); lines.push(`${indent}- ${key}: ${valueStr}`); break; case 'changed': const oldValue = formatValue(item.value1); const newValue = formatValue(item.value2); lines.push(`${indent}- ${key}: ${oldValue}`); lines.push(`${indent}+ ${key}: ${newValue}`); break; case 'nochanged': valueStr = formatValue(item.value2); lines.push(`${indent} ${key}: ${valueStr}`); break; } }); } // 处理纯对象(如group2的value,非节点数组) else if (typeof data === 'object' && data !== null) { Object.entries(data).forEach(([k, v]) => { const valStr = formatValue(v); lines.push(`${innerIndent}${k}: ${valStr}`); }); } // 包裹成对象结构 if (Array.isArray(data) || (typeof data === 'object' && data !== null)) { return [ '{', ...lines, `${indent}}` ].join('\n'); } return formatValue(data); }; return iter(data, 0); };
修正原理说明
新增类型判断逻辑
- 新增
formatValue函数,区分简单值、纯对象、节点结构:- 直接返回
null、空字符串的格式化结果 - 对纯对象自动调用
iter进行递归格式化,避免[object Object]
- 直接返回
iter函数区分节点数组、纯对象两种输入类型,分别处理
- 新增
修复递归与缩进问题
- 递归调用时
depth递增,确保子节点缩进层级正确 - 定义
indent(当前层级缩进)和innerIndent(子节点缩进),统一格式对齐
- 递归调用时
修正输出格式
- 移除键值对冒号后的多余空格,与目标输出一致
- 对
added/deleted类型的简单值直接输出,不再递归生成空对象
完善复杂值处理
- 对
changed类型中的对象值(如nest的value1)进行递归格式化,输出正确的树形结构
- 对
JavaScript对象、数组学习资源
书籍
- 《JavaScript高级程序设计(第4版)》:全面覆盖JS对象、数组的底层原理、API与实践技巧
- 《你不知道的JavaScript(上卷)》:深入讲解JS对象的原型链、继承等核心概念,帮你建立底层认知
- 《JavaScript权威指南(第7版)》:详尽的JS参考手册,包含对象、数组所有API的详细说明与示例
网站
- MDN Web Docs:官方文档,对象、数组的每个方法都有清晰的示例与使用场景说明
- 现代JavaScript教程:结构化讲解JS核心知识,对象、数组部分通俗易懂,适合系统学习
- JS数组方法大全:整理了所有数组操作方法,附带实用示例,方便快速查询
内容的提问来源于stack exchange,提问作者Sabir
相关产品推荐
相关产品推荐

