JavaScript递归报错RangeError:调用栈超出最大值问题排查
解决递归函数的RangeError: Maximum call stack size exceeded问题
首先看你的代码里有个非常明显的错误,直接导致了递归逻辑混乱,甚至触发无限递归:在for...of循环中,你错误地传递了children[i]作为递归调用的参数,但实际上i本身就是children数组里的子登录项key。
问题根源拆解
你的循环代码片段:
for (let i of children) { sublogins = sublogins.concat(traverse_all_sublogins(flat, children[i])); }
for...of遍历的是children数组的元素(比如每个子账号的标识,像"account_3"),而不是数组索引。children[i]相当于用元素值当数组索引去取内容,这大概率会得到undefined(如果元素是字符串类型的账号key),或者拿到完全错误的节点。这种错误会引发两种情况:
- 如果错误传递的参数意外指向了父节点或者当前节点,就会触发无限递归,反复调用同一个节点的处理逻辑,最终耗尽调用栈抛出错误。
- 即使没有循环引用,错误的参数也会让你无法正确遍历所有子节点,同时可能因为重复处理某些节点导致栈溢出。
修复后的基础版本
把递归参数改成i即可修正核心问题:
window.traverse_all_sublogins = (flat, LOGIN) => { let sublogins = [LOGIN]; if (flat[LOGIN] && flat[LOGIN].children) { let children = flat[LOGIN].children; for (let i of children) { // 这里把children[i]替换成i,直接传递当前子节点key sublogins = sublogins.concat(traverse_all_sublogins(flat, i)); } } return sublogins; };
额外的防溢出保险方案
如果你的flat数据结构中可能存在循环引用(比如某个账号的children包含自己,或者子账号的children指向父账号),哪怕修复了参数,递归还是会无限循环导致栈溢出。这时候可以添加一个已访问节点集合来避免重复处理:
window.traverse_all_sublogins = (flat, LOGIN, visited = new Set()) => { // 若节点已处理过,直接返回空数组终止递归分支 if (visited.has(LOGIN)) return []; visited.add(LOGIN); let sublogins = [LOGIN]; if (flat[LOGIN] && flat[LOGIN].children) { let children = flat[LOGIN].children; for (let i of children) { sublogins = sublogins.concat(traverse_all_sublogins(flat, i, visited)); } } return sublogins; };
这个版本通过visited集合记录已处理的节点,遇到重复节点时直接返回,从根源上避免无限递归。
内容的提问来源于stack exchange,提问作者aaa
相关产品推荐
相关产品推荐

