为何.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
相关产品推荐
相关产品推荐

