导读:本期聚焦于小伙伴创作的《如何递归遍历任意嵌套的多维数组(支持无限层级)》,敬请观看详情。为什么用普通for循环处理后台返回的JSON树状数据总会漏掉深层节点?根本原因在于嵌套结构在编译期无法确定深度。递归函数的执行依赖于调用栈自动保存每一层的上下文,只要当前元素仍是数组就继续向下深入,直到命中叶子节点才执行业务逻辑。相比扁平化后再迭代,直接递归能保留原始路径信息,方便追溯数据来源。不过要注意最大调用栈限制,在极端深层级场景下可改为显式栈模拟递归。下面以PHP与JavaScript为例拆解实现细节。

在后端接口对接或配置文件解析时,我们经常会遇到结构不固定的多维数组,外层可能包裹着若干层子数组,且深度完全不可预知。要完整提取其中的每一个标量值或对象,仅靠单层循环无法覆盖所有分支,必须借助函数自身调用自身的方式逐层下探。

如何递归遍历任意嵌套的多维数组(支持无限层级)

一、递归遍历的核心原理

递归遍历的本质是“分而治之”。当程序遇到一个数组元素时,先判断它是否还是数组类型;如果是,就把它交给同一个遍历函数再次处理;如果不是,说明已经抵达最底层,可以直接读取或处理该值。这种自我调用的过程会在内存的调用栈中不断压入新的函数帧,直到所有分支都走到叶子节点后依次返回。

理解调用栈很关键。每一次递归调用都会保留当前层的索引与上下文,因此天然记录了“从根到当前节点”的路径。比起先把数组强行拉平再处理,递归能让你在回调中轻松拿到层级深度与祖先信息,对于生成树形菜单、计算节点权重等需求非常友好。当然,如果层级深到突破引擎限制(例如PHP默认数组递归过深会报段错误,JavaScript在万层以上可能爆栈),就需要改用后面提到的显式栈方案。

1.1 判断类型的细微差别

不同语言对“数组”的定义并不一致。在JavaScript里,数组是Array的实例,但后端常把对象也当关联数组用;在PHP中既有索引数组也有关联数组,用is_array就能统一识别。写递归前必须先明确:什么样的容器需要继续往下走,什么样的终点值需要被收集。

以JavaScript为例,如果用typeof判断,null也会返回object,造成误判。更稳妥的做法是Array.isArray()或者借助Object.prototype.toString.call()。只有在确认是数组(或可迭代集合)时才递归,否则执行叶子逻辑,这样才能避免死循环或类型错误。

二、JavaScript中的递归实现

下面是一段通用的JS递归遍历函数,它不关心数组有多少层,只要发现子元素是数组就继续深入,同时把当前路径传给回调,方便业务侧知道这个值处在哪一枝节点上。

function traverseArray(data, callback, path = []) {
  if (Array.isArray(data)) {
    data.forEach(function (item, index) {
      var currentPath = path.concat(index);
      traverseArray(item, callback, currentPath);
    });
  } else {
    callback(data, path);
  }
}

var nested = [1, [2, [3, 4]], [[5], 6]];
traverseArray(nested, function (value, path) {
  console.log('值:' + value + ' 路径:' + path.join('->'));
});

上面的代码把每一层的索引拼成路径数组,在叶子节点触发callback。运行后会依次输出1、2、3、4、5、6以及它们各自的索引轨迹。由于函数内部始终用Array.isArray做守卫,即使数据里混着字符串、数字或null也不会中断。

如果你希望在遍历时顺带修改原数组,只需在递归前对data[index]重新赋值即可。但要注意,递归修改共享引用会带来副作用,大型项目中更推荐返回新结构而非直接改动入参,这样便于调试和做不可变数据管理。

2.1 使用生成器优化内存

当数组极其庞大且只需逐个消费时,可以用Generator把递归改成惰性产出,避免一次性构建结果集占用大量内存。

function* flattenGen(arr) {
  for (var i = 0; i < arr.length; i++) {
    if (Array.isArray(arr[i])) {
      yield* flattenGen(arr[i]);
    } else {
      yield arr[i];
    }
  }
}

var big = [1, [2, [3, [4, 5]]]];
for (var v of flattenGen(big)) {
  console.log(v);
}

生成器在遇到yield时暂停,调用方每次取一个值,特别适合流式处理日志或逐行写文件的场景。它仍然依赖调用栈保存状态,但不会因为收集全部结果而撑大堆内存。

三、PHP中的递归遍历示例

PHP没有Generator也能轻松写递归,且is_array可以识别一切数组形态。下面示例演示如何打印任意嵌套数组的所有标量,并标注深度。

function walk_deep($data, $depth = 0) {
  if (is_array($data)) {
    foreach ($data as $key => $val) {
      walk_deep($val, $depth + 1);
    }
  } else {
    echo str_repeat('--', $depth) . $data . PHP_EOL;
  }
}

$list = array(1, array(2, array(3, 4)), array(array(5), 6));
walk_deep($list);

这段代码利用$depth参数跟踪层级,遇到数组就加一层并继续,遇到字符串或数字就按缩进输出。因为PHP的数组本身可以嵌套任意深度,只要服务器内存和栈空间足够,函数就能一直深入。

实际业务里,我们常把递归封装成类方法,在叶子节点调用用户传入的闭包,从而把“遍历”和“处理”解耦。例如电商类目树、省市区联动数据都可用同一套walk逻辑,仅替换callback就能实现搜索、统计或格式化导出。

3.1 显式栈避免爆栈

如果怀疑数据层级会超过语言栈上限,可以用数组模拟调用栈,把“待处理节点”压入队列,循环推出,这样深度只受内存限制。

function iter_deep($data) {
  $stack = array(array('val' => $data, 'depth' => 0));
  while (!empty($stack)) {
    $cur = array_pop($stack);
    if (is_array($cur['val'])) {
      foreach ($cur['val'] as $v) {
        $stack[] = array('val' => $v, 'depth' => $cur['depth'] + 1);
      }
    } else {
      echo str_repeat('--', $cur['depth']) . $cur['val'] . PHP_EOL;
    }
  }
}

iter_deep($list);

显式栈写法虽然代码稍长,但彻底摆脱了函数递归的层次束缚,在处理未知来源的文件解析结果时更稳健。它的思路与浏览器渲染树的先序遍历一致,先处理当前再展开子级。

四、常见误区与注意点

很多人在写递归时会忘记设置终止条件,或者把“判断是否为数组”写错,导致函数反复调用自身进入死循环。务必保证每一次递归调用都让数据规模变小,或层级变深直至触达非数组类型。

另一个坑是循环引用:如果数组里某个子元素间接指向了祖先,纯递归会无限转圈。可在遍历时维护一个已访问对象的WeakMap或spl_object_id集合,遇到重复引用直接跳过。此外,递归虽优雅,但在浅层且结构固定的数据上,展开成循环往往更快,不必为了用递归而递归。

方案优点缺点
函数递归代码简短,路径天然保留受调用栈深度限制
显式栈循环支持极深结构,不易爆栈需手动管理状态,稍繁琐
生成器递归惰性产出,省内存语言需支持Generator

综上所述,面对无限层级的多维数组,优先用语言内置的数组判断加自我调用实现递归;当深度或内存成为瓶颈时,再切换到显式栈或生成器。理清“什么是容器、什么是叶子”这一边界,就能写出既安全又易维护的遍历逻辑。

递归遍历多维数组无限层级修改时间:2026-08-03 07:48:38

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。