在前端開發實務中,處理 JS 陣列加總 是非常高頻的常見需求——在實作電商購物車總金額、訂單報表統計或評分平均時,陣列數字加總(Array Sum)與平均值計算是前端最常見的資料處理邏輯。繼先前介紹 JS 陣列取得最大值與最小值的 5 種方法 後,本文將深入拆解
Array.prototype.reduce()在純數字陣列與「物件陣列(Array of Objects)」求和時的初始值陷阱與型別防呆。
一、 核心複製即用範本與陣列加總方法對照表
針對不同的陣列結構(純數字陣列 vs 購物車商品物件陣列),以下整理最常用的加總與平均計算方式:
| 計算需求場景 | 建議語法結構 | 空陣列 [] 回傳值 |
|---|---|---|
| 純數字陣列加總(Sum) | arr.reduce((acc, cur) => acc + Number(cur), 0) | 0(安全不報錯) |
| 物件陣列特定欄位加總 | items.reduce((acc, item) => acc + item.price * item.qty, 0) | 0(安全不報錯) |
| 陣列平均值計算(Average) | arr.length === 0 ? 0 : sum / arr.length | 0(防範 0 / 0 = NaN) |
| 超大陣列迴圈加總 | for (const n of arr) sum += n | 0(無回呼函式開銷) |
二、 分項語法拆解與 reduce 初始值陷阱
1. 純數字陣列加總:為什麼 reduce 一定要寫第二個參數 0?
使用 Array.prototype.reduce((accumulator, currentValue) => accumulator + currentValue, initialValue) 時,許多新手會省略最後面的初始值 0。
省略初始值在陣列有資料時看似正常,但一旦後端 API 回傳空陣列 [],省略初始值的 reduce 就會直接拋出 TypeError: Reduce of empty array with no initial value 導致整個頁面崩潰!
const prices = [120, 250, 80, 50];
// ✅ 務必傳入第二個參數 0 作為初始累加值 (initialValue)
const total = prices.reduce((acc, cur) => acc + cur, 0);
console.log(total); // 輸出: 500
// 當遇到空陣列時:
const emptyList = [];
console.log(emptyList.reduce((acc, cur) => acc + cur, 0)); // 輸出: 0(正常運作)
// ❌ 若未設定初始值 0,遇到空陣列會直接拋出 TypeError 崩潰!
// emptyList.reduce((acc, cur) => acc + cur);
// -> Uncaught TypeError: Reduce of empty array with no initial value
2. 物件陣列(購物車商品清單)金額加總
在真實專案中,API 回傳的資料幾乎都是物件陣列(例如 [{ name: '滑鼠', price: 500, qty: 2 }, ...])。如果這時忘記傳入初始值 0,reduce 會把第一個物件當成初始值 acc,並從第二個元素開始執行回呼,第一次就是 物件 + 數字,物件被轉成字串 "[object Object]" 後一路變成字串串接。以下方的 cartItems 為例,省略初始值會得到 "[object Object]1700300",而不是數字 4400!
const cartItems = [
{ id: 1, name: '機械鍵盤', price: 2400, qty: 1 },
{ id: 2, name: '無線滑鼠', price: 850, qty: 2 },
{ id: 3, name: '滑鼠墊', price: 300, qty: 1 },
];
// ✅ 初始值設為 0,每次累加單價乘以數量
const cartTotal = cartItems.reduce((acc, item) => {
const price = Number(item.price) || 0;
const qty = Number(item.qty) || 0;
return acc + price * qty;
}, 0);
console.log(cartTotal); // 輸出: 4400 (2400*1 + 850*2 + 300*1)
算出總金額 4400 後,若要在結帳畫面顯示為 "4,400",可直接銜接 JS 數字加逗號與轉換千分位顯示。
3. 計算陣列平均值(Average)與空陣列除以零防呆
計算平均值的公式為「總和除以陣列長度(sum / arr.length)」。需特別注意當陣列為空(arr.length === 0)時,0 / 0 在 JavaScript 中會算出 NaN,因此必須先檢查陣列長度:
/**
* 計算數字陣列的總和與平均值(自動略過 null、undefined 與非數字項目)
* @param {Array} arr - 原始陣列
* @returns {{ sum: number, average: number, validCount: number }}
*/
function getArrayStats(arr) {
if (!Array.isArray(arr) || arr.length === 0) {
return { sum: 0, average: 0, validCount: 0 };
}
let sum = 0;
let validCount = 0;
for (const item of arr) {
// 只接受數字或非空白字串,避免 true、[]、' ' 被 Number() 轉成 1 或 0 混進計算
const isNumberLike =
typeof item === 'number' || (typeof item === 'string' && item.trim() !== '');
if (isNumberLike && Number.isFinite(Number(item))) {
sum += Number(item);
validCount++;
}
}
const average = validCount === 0 ? 0 : sum / validCount;
return { sum, average, validCount };
}
const scores = [85, 92, '78', null, 95];
const stats = getArrayStats(scores);
console.log(stats);
// 輸出: { sum: 350, average: 87.5, validCount: 4 }
(註:若想事先將陣列中的 null 與空值清理乾淨,亦可參考 JS 去除 array 中 null 或空值;若平均值需要無條件進位,可搭配 JavaScript Math.ceil 無條件進位。)
三、 常見問題與排錯 Q&A
Q1:為什麼陣列加總結果突然變成一長串數字字串(如 "0100200300")?
- 問題原因:陣列中的元素來自表單
<input>或 URL 參數,型別實際上是字串(如["100", "200", "300"])。當初始值為數字0,第一圈0 + "100"就會觸發字串串接變成"0100"! - 解決思路:在累加時務必明確執行
acc + Number(cur)將當前元素轉為數字型別。
Q2:在 React JSX 中可以直接渲染 items.reduce(...) 的結果嗎?
- 問題原因:可以,只要確保
reduce的初始值為0且內部做好Number()防呆,不會算出NaN。 - 解決思路:若
items資料量較大且元件頻繁重新渲染,建議可搭配useMemo快取計算結果,或將格式化後的千分位字串(如.toLocaleString())一併封裝輸出。
參考資料與延伸閱讀
- MDN Web Docs: Array.prototype.reduce()
- JS 陣列取得最大值與最小值的 5 種方法
- JS 去除 array 中 null 或空值
- JS 數字加逗號與轉換千分位顯示
- JavaScript Math.ceil 無條件進位:小數點第 N 位、十位數進位公式與分頁總頁數計算
code
發佈留言