在 JavaScript 中操作統計數據或繪製 Chart.js 圖表 時,實作 JS 陣列取得最大值與最小值是非常高頻的需求。由於原生 Math.max() 與 Math.min() 不直接接受陣列作為單一參數,本文整理 5 種將陣列轉換求取極值的實作方式,並比較各方法在面對超大型陣列時的記憶體與 Call Stack 限制。
⚡ 快速跳轉(依實務情境選擇):
一、 JS 陣列取最大值核心語法對照
在處理陣列極值時,開發者通常在「語法簡潔度」與「執行穩定性」之間做選擇:
// 【最簡潔】Spread Operator:適用於一般中小型陣列 (筆數 < 6 萬)
const max = Math.max(...numbers);
// 【最安全】for...of 迴圈:適用於超大型陣列,效能最高且不觸發 Call Stack 溢位
let max = -Infinity;
for (const num of numbers) { if (num > max) max = num; }
1.2 5 種 JS 陣列取最大值與最小值方法比較表
| 方法名稱 | 核心語法範例 | 超大陣列安全性(> 10 萬筆) | 適用場景 |
|---|---|---|---|
1. for...of 迴圈 | if (num > max) max = num | 安全(無參數限制,效能最高) | 任何規模陣列、大數據統計 |
| 2. Spread 展開運算子 | Math.max(...arr) | 注意(超過上限拋出 RangeError) | 現代 ES6+ 中小型陣列首選 |
3. reduce 迭代 | arr.reduce((a, b) => ...) | 安全(逐一比較,無參數限制) | 函式編程、處理物件陣列 |
4. Math.max.apply | Math.max.apply(null, arr) | 注意(超過上限拋出 RangeError) | 舊版環境或中小型陣列 |
二、 5 種取得陣列最大值/最小值實作解析
1. 使用 for...of 迴圈逐一比較(最穩定、無筆數上限)
最基礎且執行效率最高的作法,是將初始最大值設為 -Infinity、初始最小值設為 Infinity,再透過單次迴圈完成比較。此方法時間複雜度為 O(n),即使陣列有上百萬筆資料,也不會發生 Call Stack 溢位錯誤。
const numbers = [10, 5, 8, 12, 3];
let max = -Infinity;
let min = Infinity;
for (const num of numbers) {
if (num > max) max = num;
if (num < min) min = num;
}
console.log(`Max: ${max}, Min: ${min}`); // Max: 12, Min: 3
2. 使用展開運算子 Math.max(...numbers)(語法最簡潔)
在 ES6 之後,可透過展開運算子(Spread Operator ...)直接將陣列在 Math.max() 括號內展開為獨立參數。這是目前開發者最常用的簡潔寫法。
const numbers = [10, 5, 8, 12, 3];
const max = Math.max(...numbers);
const min = Math.min(...numbers);
⚠️ 注意事項:展開運算子底層是將陣列元素拆解為函式呼叫的參數。如果陣列元素超過約 65,536 筆(依瀏覽器引擎上限而定),會直接拋出 RangeError: Maximum call stack size exceeded。
3. 使用 Array.prototype.reduce
使用 reduce 方法可以逐一對陣列元素進行兩兩比較,既具備函式編程風格,也不會受到函式參數數量上限的限制。
const numbers = [10, 5, 8, 12, 3];
const max = numbers.reduce((a, b) => Math.max(a, b), -Infinity);
const min = numbers.reduce((a, b) => Math.min(a, b), Infinity);
4. 使用 Math.max.apply 與 Math.min.apply
在 ES5 時代最經典的寫法,將陣列元素拆解為參數清單傳入。其限制與展開運算子相同,不建議用於超大型陣列。
const numbers = [10, 5, 8, 12, 3];
const max = Math.max.apply(null, numbers);
5. 使用 sort 排序
先使用 [...numbers] 淺拷貝一份陣列(避免修改原始資料),排序後取首尾值。
const numbers = [10, 5, 8, 12, 3];
const sorted = [...numbers].sort((a, b) => a - b);
const min = sorted[0];
const max = sorted[sorted.length - 1];
三、 常見問題與排錯 Q&A
Q1:如果陣列裡面混有 null 或 undefined,會算出什麼?
- 問題原因:
Math.max()會執行Number()轉型。Number(null)會變成0,而Number(undefined)會變成NaN。若陣列中包含undefined,結果會直接變成NaN;若包含null,則可能誤判最大值為 0。 - 解決思路:在運算前先過濾掉非數值。詳細作法可參考 JS 去除 array 中 null 或空值。
const mixed = [10, null, -5, undefined];
const clean = mixed.filter(n => typeof n === 'number');
const max = Math.max(...clean); // 10
Q2:如何安全處理「空陣列」避免噴錯或回傳 Infinity?
- 問題原因:
Math.max(...[])會回傳-Infinity,而[].reduce()若未提供初始值會拋出TypeError。 - 解決思路:務必檢查陣列長度,或在
reduce中提供明確的初始值。
const emptyArr = [];
// 1. 檢查長度
const max = emptyArr.length > 0 ? Math.max(...emptyArr) : null;
// 2. reduce 初始值 (最安全)
const safeMax = emptyArr.reduce((a, b) => Math.max(a, b), -Infinity);
// 若為空陣列會回傳 -Infinity,代表無最大值
Q3:為什麼用 sort() 不寫比較函式時,最大值會判斷錯誤?
- 問題原因:
Array.prototype.sort()預設會將元素轉為字串排列。例如"10"會排在"5"前面,因為字元"1"的編碼小於"5"。 - 解決思路:對數字陣列執行
sort時,務必傳入(a, b) => a - b。
發佈留言