You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

递归找零函数出现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);
  }
});

关键改动点说明

  1. 递归→循环:用while循环替代递归调用,彻底消除栈溢出风险,循环不会占用调用栈空间,多少次迭代都没问题。
  2. 浮点精度优化:
    • 用remainingValue > 0.001代替remainingValue == 0,因为JavaScript的浮点运算有精度误差(比如0.1+0.2≠0.3),直接判断等于0可能会导致死循环。
    • 每次计算后都用toFixed(2)格式化金额,确保始终保留两位小数,避免精度漂移。
  3. 代码健壮性提升:把returnList的重置移到函数内部,避免外部变量的旧数据干扰新的计算;同时增加了找零金额为负的判断,避免无效计算。

补充说明

为啥递归不行?因为每次递归调用都会在JavaScript的调用栈里新增一个调用帧,栈的容量是有限的(不同浏览器略有差异,但大多在10000次左右)。大金额找零需要的递归次数远超这个上限,自然就栈溢出了。而循环是在同一个执行上下文里重复执行,不会产生新的调用帧,所以没有这个限制。

内容的提问来源于stack exchange,提问作者Indraraj26

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 12:22:43