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

递归函数不是“必须用循环替代的炫技写法”,而是解决**天然具有自相似结构的问题**最直接的方式——比如遍历树、解析嵌套对象、计算阶乘、实现快速排序等。关键不在“能不能写”,而在“要不要写”和“会不会爆栈”。
递归函数的两个必要条件
所有安全可用的递归函数都必须同时满足:
- 有一个或多个
base case(基础情况),即无需再次调用自身就能直接返回结果的条件; - 每次递归调用都必须向
base case靠近,否则会无限调用直至触发RangeError: Maximum call stack size exceeded。
JavaScript 中写递归函数的典型结构
以计算阶乘为例:n! = n × (n−1)!,而 0! = 1 是自然终止点。
factorial(5)
- 忘记检查
base case或写错条件(如用n 却没处理负数,导致factorial(-1)死循环); - 递归调用未改变参数(如写成
factorial(n)而非factorial(n - 1)); - 对深层嵌套对象递归遍历时,没检测循环引用,引发无限展开(
JSON.stringify就因此抛错); - 在事件处理或定时器中无节制递归(如
setTimeout(fn, 0)+ 立即调用fn()),表面像递归实则失控。
什么时候该避免递归?
当问题可以线性迭代完成,且没有明显分治/嵌套结构时,优先用 for 或 while。例如遍历数组求和、字符串翻转、简单累加——这些用递归不仅慢,还占栈空间,也没有可读性优势。
真正值得递归的场景,是代码逻辑和问题结构完全对齐:比如解析一个 menu 对象,它有 children 数组,每个子项又可能有 children……这时候一个 renderMenu(item) 函数调自己一次,比写三层 for 嵌套清晰得多。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











