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

为何.reduce()会修改外部声明的初始值,内部声明则不会?

问题:数组声明位置导致的支出累加异常

我正在按一周7天对所有支出进行分组。首先在constant.ts中声明如下数组:

const weeklySchedule = [
  {
    label: "Monday",
    value: 0,
  },
  {
    label: "Tuesday",
    value: 0,
  },

  ...

  {
    label: "Sunday",
    value: 0,
  },
];

随后在Expense.tsx中创建函数:

const getWeeklyExpenses = (expenses: Expense[]) => {

  const groupedExpenses = expenses.reduce((acc, expense) => {
    const day = dayNumberNames[expense.date.getDay()]; // 返回星期几
    const existing = acc.find((e) => e.label === day);

    existing.value += expense.amount;

    return acc;
  }, weeklySchedule);

  return groupedExpenses;
};

发现每次修改代码后,weeklySchedule中的值会持续累加(被意外修改),但将weeklySchedule声明在函数内部时,该问题就不会出现且功能正常。请问这两种变量声明方式有何差异?


原因分析

这两种声明方式的核心差异在于变量作用域和引用类型的内存特性:

  • 模块级全局声明(constant.ts中)
    weeklySchedule是模块级常量,程序启动后只会创建唯一的数组实例,所有调用getWeeklyExpenses的地方都会复用这个实例。数组属于引用类型,reduce里的acc指向的就是这个全局数组的内存地址,修改existing.value本质是直接修改了全局数组中对象的属性值,下次调用函数时初始值已不是0,因此会持续累加。

  • 函数内部声明
    每次调用getWeeklyExpenses时,都会重新创建一个全新的weeklySchedule数组实例,初始值均为0。reduce操作的是这个新数组,修改的是新数组内对象的属性,不会影响其他调用或全局状态,所以每次计算都是从0开始,结果符合预期。

如果想保留全局声明的方式又避免污染原数组,可以在reduce的初始值中创建原数组的浅拷贝(因为数组元素是对象,需要拷贝每个对象):

const getWeeklyExpenses = (expenses: Expense[]) => {
  // 生成全新的初始数组,每个对象独立拷贝
  const initialAcc = weeklySchedule.map(item => ({ ...item }));
  const groupedExpenses = expenses.reduce((acc, expense) => {
    const day = dayNumberNames[expense.date.getDay()];
    const existing = acc.find((e) => e.label === day);
    existing.value += expense.amount;
    return acc;
  }, initialAcc);

  return groupedExpenses;
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 22:57:39