递归删除对象数组内嵌套目标节点时为何出现最大调用栈超限报错
栈溢出根因定位
原实现的无限递归是函数逻辑和调用方式完全错位导致的:
- 递归调用时传入的
entry.children、currentId两个实参,根本没有被原_worktreeItemDelete函数接收——该函数的定义只预留了{ detail }这一个事件对象入参位置,完全忽略了递归传值。 - 每次递归触发时,函数都会重新读取组件初始的
this.tree数据源、重复执行状态更新逻辑,既没有处理传入的子树参数,也没有终止递归的判断出口,最终形成无终止的自调用循环,撑爆调用栈。 - 核心设计问题:把「纯递归遍历删除的无副作用逻辑」和「绑定事件、更新组件状态的副作用逻辑」混在同一个函数里,既没法正确传参给递归调用,还会在递归过程中重复触发状态更新。
修正实现方案
将功能拆分为两个独立部分,从根源解决递归传参和终止条件问题:
- 独立纯工具函数:不依赖组件实例状态,只接收待遍历树、待删除ID两个参数完成递归删除,设置清晰的递归终止条件
- 事件入口函数:仅负责从事件对象中提取待删除ID,调用纯工具函数计算新树结构,触发组件状态更新
修正后的完整代码实现:
// 纯递归删除工具:无外部依赖、无副作用 function deleteNodeRecursively(tree, targetId) { // 过滤当前层级和目标ID匹配的节点 const currentLevelFiltered = tree.filter(node => node.id !== targetId) // 处理保留节点的子树 return currentLevelFiltered.map(node => { // 递归终止条件:节点没有子节点时直接返回,不再向下遍历 if (!node.children?.length) return node // 存在子节点则递归调用自身处理子树 return { ...node, children: deleteNodeRecursively(node.children, targetId) } }) } // 事件触发入口:仅处理事件逻辑、状态更新 function _worktreeItemDelete({ detail }) { const currentId = detail.item.id const updatedTree = deleteNodeRecursively(this.tree, currentId) this.set("tree", updatedTree) }
内容的提问来源于stack exchange,提问作者Ian
相关产品推荐
相关产品推荐

