递归是解决自相似结构问题的自然表达方式;写错主因是缺终止条件或参数错误,base case作为“刹车”须明确,如阶乘中factorial(0)返回1、树遍历遇null返回、字符串反转以空串为base case。

递归不是“必须用循环替代的高级写法”,而是**解决具有自相似结构问题的自然表达方式**;在 JavaScript 中写错递归,八成是因为忘了终止条件或误传参数。
递归函数必须有明确的 base case
没有终止条件的递归会一直调用自身,直到栈溢出报错 RangeError: Maximum call stack size exceeded。base case 是递归的“刹车”,它不调用自身,直接返回结果。
- 比如计算阶乘:
factorial(0)或factorial(1)应直接返回1,不能继续调factorial(-1) - 遍历树结构时,遇到
null或叶子节点(无子节点)就要 return,不能对node.children做无条件递归 - 字符串处理中,空字符串
""常是天然的 base case,比如反转字符串:if (str.length
每次递归调用必须向 base case 靠拢
光有 base case 不够,还要确保每次调用都在缩小问题规模。否则即使写了 if (n === 0) return 1,若调用写成 factorial(n) 而非 factorial(n - 1),照样死循环。
- 数组操作:递归处理
arr.slice(1)或传入索引i + 1,而不是反复传整个原数组 - 数字计算:用
n - 1、Math.floor(n / 2)等可收敛的变换,避免n - 0.5(浮点误差可能导致永远达不到 0) - 对象/树遍历:只对已知存在的子节点递归,比如
node.left && traverse(node.left),而非盲目访问node.left后再判空
JavaScript 中注意调用栈与尾递归的实际限制
ES6 规范支持尾递归优化(TCO),但**目前仅 Safari 默认启用,Chrome 和 Firefox 已移除 TCO 支持**。这意味着即使写成尾递归形式,也无法避免栈增长。
立即学习“Java免费学习笔记(深入)”;
- 尾递归写法(看似优化):
function sum(n, acc = 0) { return n === 0 ? acc : sum(n - 1, acc + n); }—— 在 Chrome 中仍会栈溢出 - 深度超过 ~10000 层的递归,在多数引擎中都会触发
RangeError - 真实项目中,若预估深度较大(如解析深层嵌套 JSON、DOM 树遍历),优先考虑显式栈(
Array.push()/.pop())或迭代重写
递归最易被忽略的不是语法,而是“问题是否真具备递归结构”——强行把线性任务(如累加数组)写成递归,除了增加栈开销和调试难度,几乎没有收益。











