You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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);
};

修正原理说明

  1. 新增类型判断逻辑

    • 新增formatValue函数,区分简单值、纯对象、节点结构:
      • 直接返回null、空字符串的格式化结果
      • 对纯对象自动调用iter进行递归格式化,避免[object Object]
    • iter函数区分节点数组、纯对象两种输入类型,分别处理
  2. 修复递归与缩进问题

    • 递归调用时depth递增,确保子节点缩进层级正确
    • 定义indent(当前层级缩进)和innerIndent(子节点缩进),统一格式对齐
  3. 修正输出格式

    • 移除键值对冒号后的多余空格,与目标输出一致
    • 对added/deleted类型的简单值直接输出,不再递归生成空对象
  4. 完善复杂值处理

    • 对changed类型中的对象值(如nest的value1)进行递归格式化,输出正确的树形结构

JavaScript对象、数组学习资源

书籍

  • 《JavaScript高级程序设计(第4版)》:全面覆盖JS对象、数组的底层原理、API与实践技巧
  • 《你不知道的JavaScript(上卷)》:深入讲解JS对象的原型链、继承等核心概念,帮你建立底层认知
  • 《JavaScript权威指南(第7版)》:详尽的JS参考手册,包含对象、数组所有API的详细说明与示例

网站

  • MDN Web Docs:官方文档,对象、数组的每个方法都有清晰的示例与使用场景说明
  • 现代JavaScript教程:结构化讲解JS核心知识,对象、数组部分通俗易懂,适合系统学习
  • JS数组方法大全:整理了所有数组操作方法,附带实用示例,方便快速查询

内容的提问来源于stack exchange,提问作者Sabir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 19:42:01