什么是递归函数_如何在javascript中编写一个递归函数【教程】

8次阅读

递归函数是解决天然具有自相似结构问题最直接的方式,需满足两个条件:存在基础情况(base case)且每次递归必须逼近该情况,否则将爆

什么是递归函数_如何在javascript中编写一个递归函数【教程】

递归函数不是“必须用循环替代的炫技写法”,而是解决**天然具有自相似结构的问题**最直接的方式——比如遍历树、解析嵌套对象、计算阶乘、实现快速排序等。关键不在“能不能写”,而在“要不要写”和“会不会爆”。

递归函数的两个必要条件

所有安全可用的递归函数都必须同时满足:

  • 有一个或多个 base case(基础情况),即无需再次调用自身就能直接返回结果的条件;
  • 每次递归调用都必须向 base case 靠近,否则会无限调用直至触发 RangeError: Maximum call stack size exceeded

javaScript 中写递归函数的典型结构

以计算阶乘为例:n! = n × (n−1)!,而 0! = 1 是自然终止点。

function factorial(n) {   if (n < 0) return NaN;           // 非法输入防护   if (n === 0 || n === 1) return 1; // base case   return n * factorial(n - 1);     // 递归调用,n 严格减小 }

注意:factorial(5) 会生成 5 层调用栈,factorial(10000) 很可能崩溃——V8 引擎默认栈深度约 10000~15000,但实际能跑多深取决于环境和调用链长度。

立即学习Java免费学习笔记(深入)”;

常见误用与坑点

递归在 js 中容易被写成“伪递归”或“危险递归”,尤其在处理异步、对象引用、数组索引时:

  • 忘记检查 base case 或写错条件(如用 n 却没处理负数,导致 factorial(-1) 死循环);
  • 递归调用未改变参数(如写成 factorial(n) 而非 factorial(n - 1));
  • 对深层嵌套对象递归遍历时,没检测循环引用,引发无限展开(JSON.stringify 就因此抛错);
  • 在事件处理或定时器中无节制递归(如 setTimeout(fn, 0) + 立即调用 fn()),表面像递归实则失控。

什么时候该避免递归?

当问题可以线性迭代完成,且没有明显分治/嵌套结构时,优先用 forwhile。例如遍历数组求和、字符串翻转、简单累加——这些用递归不仅慢,还占栈空间,也没有可读性优势。

真正值得递归的场景,是代码逻辑和问题结构完全对齐:比如解析一个 menu 对象,它有 children 数组,每个子项又可能有 children……这时候一个 renderMenu(item) 函数调自己一次,比写三层 for 嵌套清晰得多。

text=ZqhQzanResources