JavaScript收银程序:重复元素求和错误及数组意外变更问题
咱们先拆解你遇到的两个典型坑,都是JavaScript数组操作和函数集成里容易踩的雷:
问题1:原change数组被意外修改——浅拷贝的锅
你说已经复制了change数组,但原数组还是被改了,这是因为二维数组的浅拷贝只会复制外层数组的引用,内层子数组还是共享内存。比如用const newArr = change.slice()或者const newArr = [...change],看起来是复制了数组,但里面的每个["TWENTY", 60]这类子数组,还是和原数组指向同一个内存地址,修改新数组的子元素,原数组自然会跟着变。
解决办法:深拷贝二维数组
把复制数组的代码改成深拷贝,两种简单可靠的方式:
// 方式1:遍历每个子数组,展开成新数组(推荐,性能更好) const copiedChange = change.map(item => [...item]); // 方式2:JSON序列化(适合纯数据数组,无函数/特殊对象) const copiedChange = JSON.parse(JSON.stringify(change));
这样新数组和原数组就完全独立了,修改新数组不会影响原抽屉数据。
问题2:sumDuplicates集成后求和错误——逻辑漏洞或原数组污染
你提到sumDuplicates单独运行正常,但集成后失效,还出现奇数重复时求和错误(比如3个TWENTY算出80),大概率是集成后函数依赖的数组被其他逻辑修改了,或者函数本身在遍历/累加时存在索引错乱的问题。
先排查sumDuplicates的常见错误逻辑
如果你的sumDuplicates是用splice删除重复元素来求和,很容易出现索引错乱:
// 错误示例:删除元素后未调整索引,导致漏加/重复计算 function sumDuplicates(arr) { const result = []; for (let i = 0; i < arr.length; i++) { let total = arr[i][1]; for (let j = i + 1; j < arr.length; j++) { if (arr[i][0] === arr[j][0]) { total += arr[j][1]; arr.splice(j, 1); // 删除j位置元素后,下一个元素移到j,j++会跳过它 // 这里必须加j--,否则索引会错乱 } } result.push([arr[i][0], total]); } return result; }
如果忘记加j--,处理奇数个重复元素时就会漏加,再加上原数组被修改,后续逻辑用了错误的数值,就会出现像3个TWENTY算出80的诡异结果。
更可靠的sumDuplicates实现:用对象分组求和
彻底避免修改原数组,用对象统计每个面额的总和,既不会污染原数组,也不会出现索引问题:
function sumDuplicates(arr) { const denominationMap = {}; // 遍历数组,按面额分组累加 arr.forEach(([name, amount]) => { denominationMap[name] = (denominationMap[name] || 0) + amount; }); // 把对象转成二维数组返回 return Object.entries(denominationMap); }
这个版本完全不修改原数组,不管重复次数是奇数还是偶数,求和都准确。
结合测试用例验证
你的测试用例中,找零金额是100 - 3.26 = 96.74,抽屉里TWENTY总额是60(3张)。用上面的深拷贝和分组求和方法,就能避免原数组被修改,同时保证求和逻辑准确,最终得到预期的[["TWENTY", 60]]结果。
总结修复步骤
- 把所有对change数组的复制操作改成深拷贝,杜绝原数组被污染;
- 替换sumDuplicates函数为分组求和的版本,不要在求和时修改原数组;
- 检查找零逻辑中是否有不小心修改原抽屉数组的代码,确保所有操作都基于拷贝后的数组。
内容的提问来源于stack exchange,提问作者Hasan Aga

