JavaScript 中实现递归遍历的 Generator 函数最佳实践

技术百科 碧海醫心 发布时间:2026-01-28 浏览:

本文介绍如何用简洁、符合标准的方式实现树形结构的递归迭代器,推荐使用纯函数式 generator(而非类封装),避免手动维护状态,同时自然支持 `for...of` 和解构语法。

在 JavaScript 中,要对嵌套结构(如带 children 的路由树)进行深度优先遍历并暴露为可迭代对象,最清晰、最符合语言惯例的做法是直接定义一个递归 generator 函数,而非构造一个需手动管理内部状态的类。你当前的 IteratorClass 虽然能运行,但存在几个关键设计隐患:

  • 违反迭代器协议最小化原则:[Symbol.iterator] 方法本应返回一个迭代器对象(即具有 next() 方法的对象),而你的实现直接返回了 generator 对象——这虽因 generator 本身实现了迭代器协议而“碰巧有效”,但掩盖了语义混淆:IteratorClass 实例并非迭代器,而是可生成迭代器的可迭代对象;真正的迭代器由 *[Symbol.iterator] 内部创建并返回。
  • 状态耦合与不可复用性:this._routes 和 this._depth 将数据与遍历深度绑定在实例上,导致同一实例无法被多次迭代(generator 一旦耗尽即不可重用),且无法灵活传入不同初始深度或定制逻辑。
  • 副作用污染数据:route.depth = this._depth 直接修改原始 route 对象,破坏了数据的不可变性,易引发隐蔽 bug。

✅ 推荐方案:使用无状态、纯函数式的递归 generator:

function* recurIter(data, depth = 1) {
  for (const node of data) {
    // 深度信息通过解构注入新对象,不修改原数据
    const { children, ...rest } = { ...node, depth };
    if (Array.isArray(children) && children.length > 0) {
      yield* recurIter(children, depth + 1); // 递归进入子树
    }
    yield rest; // 输出当前节点(含 depth)
  }
}

该函数天然满足迭代器协议:调用

recurIter(data) 即返回一个 generator 对象,该对象既是可迭代的(支持 for...of),也是迭代器(自带 next())。使用时无需实例化、无副作用、可无限复用:

const data = [
  {
    key: 1,
    path: '/users',
    name: 'users',
    children: [
      { key: 2, path: '/users/roles', name: 'roles' },
      { key: 3, path: '/users/permissions', name: 'permissions' }
    ]
  },
  {
    key: 4,
    path: '/projects',
    name: 'projects',
    children: [
      {
        key: 5,
        path: '/projects/milestones',
        name: 'milestones',
        children: [
          { key: 6, path: '/projects/milestones/tasks', name: 'tasks' }
        ]
      }
    ]
  }
];

// 安全、清晰、可解构
for (const { name, depth, path } of recurIter(data)) {
  console.log(`ROUTE: "${name}" | PATH: "${path}" | DEPTH: ${depth}`);
}
// 输出:
// ROUTE: "users" | PATH: "/users" | DEPTH: 1
// ROUTE: "roles" | PATH: "/users/roles" | DEPTH: 2
// ROUTE: "permissions" | PATH: "/users/permissions" | DEPTH: 2
// ROUTE: "projects" | PATH: "/projects" | DEPTH: 1
// ROUTE: "milestones" | PATH: "/projects/milestones" | DEPTH: 2
// ROUTE: "tasks" | PATH: "/projects/milestones/tasks" | DEPTH: 3

进阶提示

  • 若需异步遍历(如 children 来自 Promise),可改用 async function* 并配合 await yield*;
  • 如需提前终止或过滤,可在 yield* 前添加条件判断;
  • 若必须封装为类(如需附加方法),应让类仅负责配置,[Symbol.iterator] 内部仍调用纯 generator 函数,而非自行管理状态。

总之,Generator 是 JavaScript 中表达“惰性递归序列”的最自然抽象——拥抱函数式思维,远离手工 next() 和状态管理,代码将更健壮、更易测试、更符合生态惯例。


# ai  # 几个  # 进阶  # 推荐使用  # 而非  # 如需  # 迭代  # 复用  # 路由  # 递归  # 对象  # javascript  # java  # function  # 异步  # this  # node  # bug  # 封装  # 遍历  # for  # 子树  # promise  # 可迭代对象  # symbol 


相关栏目: <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 AI推广<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 SEO优化<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 技术百科<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 谷歌推广<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 百度推广<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 网络营销<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 案例网站<?muma echo $count; ?> 】 <?muma $count = M('archives')->where(['typeid'=>$field['id']])->count(); ?> 【 精选文章<?muma echo $count; ?>

相关推荐

在线咨询

点击这里给我发消息QQ客服

在线咨询

免费通话

24h咨询:4006964355


如您有问题,可以咨询我们的24H咨询电话!

免费通话

微信扫一扫

微信联系
返回顶部