紀錄工作經驗、相關知識,解決技術相關問題。

Javascript, jQuery, React, 新手 Jamp Holes, 網站相關, 資訊相關

JS 陣列取最大值、最小值教學:Math.max, reduce 與展開運算子用法

JS 陣列取最大值、最小值教學:Math.max, reduce 與展開運算子用法 特色圖片

在 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.applyMath.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。

參考資料與延伸閱讀

發佈留言