递归找零函数出现Uncaught RangeError: Maximum call stack size exceeded错误排查
解决大金额找零计算中的栈溢出问题
Hey,我看到你写的找零计算函数在处理6、7位数金额时出现了栈溢出错误,这问题其实很好解决——根源在于你用了递归处理重复计算,而JavaScript的调用栈深度是有限的,大金额会导致递归次数直接超出上限。
问题根源
当你输入1234567这样的大金额时,找零需要反复调用isRemainingMoney函数成千上万次(比如要连续减1元一万多次,甚至还要处理分币)。而浏览器的JavaScript调用栈一般只有几千到几万的深度上限,超过这个次数就会触发Maximum call stack size exceeded错误。
修复方案:把递归改成循环
递归写法虽然简洁,但完全不适合这种需要大量重复执行的场景。咱们把递归逻辑换成while循环,这样就不会占用调用栈,无论金额多大都能稳稳运行。
修改后的完整代码
let returnList = []; const predictorList = [100, 50, 20, 10, 5, 1, 0.5, 0.25, 0.1, 0.05, 0.01]; const total = 11.23; function calculateChange(value) { returnList = []; // 每次计算前清空结果列表,避免旧数据干扰 let remainingValue = +value.toFixed(2); // 先格式化到两位小数,规避浮点精度坑 // 用循环代替递归,直到剩余金额趋近于0 while (remainingValue > 0.001) { for (const pRed of predictorList) { // 加个微小偏移量,处理浮点精度导致的判断误差 if (remainingValue >= pRed - 0.001) { const existingItem = returnList.find(item => +item.money === +pRed); if (existingItem) { existingItem.count += 1; existingItem.total = +(existingItem.total + pRed).toFixed(2); } else { returnList.push({ type: pRed, money: pRed, count: 1, total: pRed }); } remainingValue = +(remainingValue - pRed).toFixed(2); break; // 找到当前最大可用面额后,跳出循环重新检查剩余金额 } } } return returnList; } document.querySelector('input').addEventListener('change', (event) => { if (event.target.value) { const changeAmount = +event.target.value - total; // 加个校验,避免金额不足的情况 if (changeAmount < 0) { console.log('金额不够,不需要找零哦'); return; } const result = calculateChange(changeAmount); console.log('找零明细:', result); } });
关键改动点说明
- 递归→循环:用
while循环替代递归调用,彻底消除栈溢出风险,循环不会占用调用栈空间,多少次迭代都没问题。 - 浮点精度优化:
- 用
remainingValue > 0.001代替remainingValue == 0,因为JavaScript的浮点运算有精度误差(比如0.1+0.2≠0.3),直接判断等于0可能会导致死循环。 - 每次计算后都用
toFixed(2)格式化金额,确保始终保留两位小数,避免精度漂移。
- 用
- 代码健壮性提升:把
returnList的重置移到函数内部,避免外部变量的旧数据干扰新的计算;同时增加了找零金额为负的判断,避免无效计算。
补充说明
为啥递归不行?因为每次递归调用都会在JavaScript的调用栈里新增一个调用帧,栈的容量是有限的(不同浏览器略有差异,但大多在10000次左右)。大金额找零需要的递归次数远超这个上限,自然就栈溢出了。而循环是在同一个执行上下文里重复执行,不会产生新的调用帧,所以没有这个限制。
内容的提问来源于stack exchange,提问作者Indraraj26
相关产品推荐
相关产品推荐

