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

ES6实现无mutation账单计算 如何规避变量突变问题

ES6无突变规范下的账单分摊函数改造方案

你当前代码中使用let声明的balance变量在迭代过程中被反复重赋值,属于函数内部的状态突变,虽然没有直接修改入参对象,但不符合纯函数无状态变更的要求。要彻底规避突变问题,核心是把迭代过程中需要传递的余额状态,和每次生成的新欠款列表共同作为reduce的累加值传递,全程不使用可重赋值的临时变量。

改造思路

  • 将处理应收方的reduce累加器从单一的欠款人数组,扩展为同时存储「当前待分配的应收余额」和「最新欠款人列表」的结构,所有状态随迭代累加器传递,不依赖外部可变变量
  • 所有对象、数组的更新全程使用展开运算符做浅拷贝,不修改任何原有引用
  • 每轮迭代处理单个欠款人时,基于传入的余额值直接计算划转金额,计算完成后返回更新后的余额和欠款列表作为下一轮迭代的输入,全程无变量重赋值

改造后完整代码

const users = [
  { id: 1, username: "John One", amount: 50 },
  { id: 2, username: "John Two", amount: 75 },
  { id: 3, username: "John Three", amount: 100 },
  { id: 4, username: "John Four", amount: 125 },
  { id: 5, username: "John five", amount: 150 },
  { id: 6, username: "John six", amount: 50 },
  { id: 7, username: "John seven", amount: 150 },
  { id: 8, username: "John eight", amount: 250 },
  { id: 9, username: "John nine", amount: 150 },
  { id: 10, username: "John Ten", amount: 100 },
]

const total = users.reduce((acc, user) => acc + user.amount, 0);
const perPersonShare = total / users.length;
const sortedUsers = users.map((user) => ({ ...user, amount: user.amount - perPersonShare, hasToPay: [] }));
const negativeAmount = sortedUsers.filter(user => user.amount < 0);
const positiveAmount = sortedUsers.filter(user => user.amount > 0);

const res = positiveAmount.reduce((acc, receiver) => {
  return acc.owers.reduce((innerAcc, ower) => {
    if (ower.amount === 0 || innerAcc.remainingReceive === 0) {
      return {
        ...innerAcc,
        owers: [...innerAcc.owers, ower]
      }
    }

    const owerOweAmount = Math.abs(ower.amount);
    if (innerAcc.remainingReceive <= owerOweAmount) {
      const updatedOwer = {
        ...ower,
        hasToPay: [...ower.hasToPay, { to: receiver.username, amount: innerAcc.remainingReceive }],
        amount: ower.amount + innerAcc.remainingReceive,
      };
      return {
        remainingReceive: 0,
        owers: [...innerAcc.owers, updatedOwer]
      }
    }

    const updatedOwer = {
      ...ower,
      hasToPay: [...ower.hasToPay, { to: receiver.username, amount: owerOweAmount }],
      amount: 0,
    };
    return {
      remainingReceive: innerAcc.remainingReceive - owerOweAmount,
      owers: [...innerAcc.owers, updatedOwer]
    }

  }, { remainingReceive: receiver.amount, owers: [] });
}, { remainingReceive: 0, owers: negativeAmount }).owers;

console.log(JSON.stringify(res, 0, 2))
console.log(perPersonShare);
console.log(total);

关键改动说明

  • 全程没有使用let声明可重赋值变量,所有状态都通过reduce累加器在迭代链路中传递,彻底消除了内部状态突变
  • 所有数组、对象更新都生成新的引用,不会修改原始入参和中间过程生成的数据集
  • 计算逻辑和原有代码完全一致,运行结果和原实现无差异

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 14:48:15