非深调用多变量场景Maximum call stack size exceeded报错求解
错误产生原因
报错的核心原因是无限递归导致调用栈溢出,原代码存在3个致命逻辑问题:
- 函数末尾无条件执行
this.noDuplicate(this.newCaption),没有设置任何递归终止条件:无论有没有检测到重名、无论生成的新标题是否合法,函数都会不停调用自身,直到超出调用栈的最大容量直接抛出错误。 - 重名处理逻辑错位:检测到重名时,无后缀分支直接修改了已存在的旧书签的
caption属性,有后缀分支仅给类属性this.newCaption赋值,没有把新生成的候选标题作为下一轮校验的核心目标,甚至在走无后缀分支时根本没给this.newCaption赋值,传入下一轮递归的参数是undefined。 - 重名校验逻辑不闭环:即使修复了递归终止条件,现有逻辑遇到多重重名(比如已存在
a、a.1、a.2,新书签标题为a)时,也无法正确生成不重复的a.3标题。
修复方案
直接用循环替代无终止条件的递归,同时修正重名处理的逻辑偏差,修复后的可运行代码如下:
newCaption: string; noDuplicate(targetCaption: string): string { let candidateCaption = targetCaption; let hasAlerted = false; // 循环校验直到找到不重复的标题 while (true) { // 检查当前候选标题是否与已有书签重名 const isDuplicated = this.preferences.bookmarks.some(item => item.caption === candidateCaption); if (!isDuplicated) break; // 仅第一次检测到重名时弹出警告 if (!hasAlerted) { this.alertService.warning("Caption is duplicate!"); hasAlerted = true; } // 生成下一轮校验的候选标题,不修改已有书签的内容 if (!candidateCaption.includes('.')) { candidateCaption = `${candidateCaption}.1`; } else { const dotPos = candidateCaption.indexOf('.'); const prefix = candidateCaption.slice(0, dotPos); const suffixStr = candidateCaption.slice(dotPos + 1); const suffixNum = parseInt(suffixStr, 10); // 兜底处理后缀不是合法数字的场景 const nextSuffix = Number.isNaN(suffixNum) ? 1 : suffixNum + 1; candidateCaption = `${prefix}.${nextSuffix}`; } } this.newCaption = candidateCaption; return candidateCaption; }
修复点说明
- 移除了无终止条件的递归,改用
while循环做重名校验,从根源上避免调用栈溢出问题,逻辑更易调试 - 全程不修改已有书签的标题属性,所有候选标题仅用于当前待校验的新书签,不会污染旧数据
- 增加了后缀非数字的兜底逻辑,遇到
test.abc这类异常格式的重名项时,会自动生成合法的test.1作为新标题,不会出现NaN的异常值 - 警告提示仅在第一次检测到重名时触发,不会重复弹出
- 只要检测到当前候选标题无重名就立刻终止流程,没有多余的性能损耗
内容的提问来源于stack exchange,提问作者Nana
相关产品推荐
相关产品推荐

