本文 AI 產出,尚未審核

JavaScript 陣列操作:forEach、map、filter、reduce、find、some、every

簡介

在 JavaScript 中,陣列是最常用的資料結構之一。無論是前端畫面渲染、資料處理,或是與後端 API 互動,都離不開對陣列的操作。ES5 以後,原生提供的 七大高階函式forEachmapfilterreducefindsomeevery)讓開發者可以用 宣告式 的方式取代傳統的 for 迴圈,寫出更簡潔、可讀性更高的程式碼。掌握這些方法不僅能提升開發效率,還能減少錯誤、提升程式的可維護性。

本篇文章將深入淺出說明每個方法的語法與使用時機,提供實務範例、常見陷阱與最佳實踐,幫助你從「會用」升級到「懂得選擇」最適合的陣列操作方式。


核心概念

1. forEach – 逐項執行副作用

forEach 會遍歷陣列的每一個元素,對每個元素呼叫一次提供的回呼函式。它不會回傳值,通常用於執行副作用(如寫入 DOM、輸出日誌等)。

const numbers = [1, 2, 3, 4, 5];

// 每個數字乘以 2 後印出
numbers.forEach((num, index) => {
  console.log(`第 ${index + 1} 個元素: ${num * 2}`);
});

注意forEach 無法中斷(不像 for 迴圈可以 break),若需要提前結束,請改用 someevery 或普通 for


2. map – 產生新陣列

map 會對每個元素執行回呼函式,回傳值會組成一個全新的陣列。適合用於「資料轉換」的情境。

const users = [
  { name: 'Alice', age: 28 },
  { name: 'Bob',   age: 34 },
  { name: 'Carol', age: 22 }
];

// 只取出名字
const names = users.map(user => user.name);
console.log(names); // ['Alice', 'Bob', 'Carol']

小技巧:若只想要簡化陣列結構map 是首選;千萬別在 map 裡做副作用,會讓程式碼混亂。


3. filter – 條件篩選

filter 會依據回呼函式返回的布林值,保留符合條件的元素,形成新陣列。

const scores = [78, 92, 61, 85, 49];

// 只保留及格(>= 60)的分數
const passed = scores.filter(score => score >= 60);
console.log(passed); // [78, 92, 61, 85]

常見錯誤:回呼函式必須回傳布林值,若寫成 return score > 60 && score,會得到不預期的結果。


4. reduce – 累積運算

reduce 可把陣列「摺疊」成任意型別的單一值(數字、物件、字串…)。它接受兩個參數:累計值 (accumulator) 和當前值 (current),以及一個可選的初始值。

const expenses = [
  { category: 'food', amount: 120 },
  { category: 'transport', amount: 80 },
  { category: 'entertainment', amount: 150 }
];

// 計算總支出
const total = expenses.reduce((sum, item) => sum + item.amount, 0);
console.log(total); // 350

技巧:若需要把陣列轉成物件映射(例如分類統計),reduce 也是好幫手。

// 依類別彙總金額
const byCategory = expenses.reduce((obj, item) => {
  obj[item.category] = (obj[item.category] || 0) + item.amount;
  return obj;
}, {});
console.log(byCategory); // { food: 120, transport: 80, entertainment: 150 }

5. find – 找到第一個符合條件的元素

find 會回傳第一個滿足條件的元素,若找不到則回傳 undefined

const products = [
  { id: 1, name: '筆記型電腦' },
  { id: 2, name: '智慧手機' },
  { id: 3, name: '平板電腦' }
];

const target = products.find(p => p.id === 2);
console.log(target); // { id: 2, name: '智慧手機' }

使用時機:只需要單一結果且不需要遍歷整個陣列時,find 會比 filter 更有效率。


6. some & every – 全部或任意符合

  • some:只要任一元素符合條件,就回傳 true
  • every全部元素都符合條件才回傳 true
const ages = [12, 25, 30, 17];

// 是否有人年滿 18 歲?
const hasAdult = ages.some(age => age >= 18);
console.log(hasAdult); // true

// 是否所有人都年滿 18 歲?
const allAdult = ages.every(age => age >= 18);
console.log(allAdult); // false

實務觀點some 常用於權限檢查(只要有一筆符合即通過),every 則適合驗證全部資料的正確性。


常見陷阱與最佳實踐

陷阱 說明 解決方式
忘記回傳值 mapfilterreduce 的回呼若未 return,會得到 undefined 或錯誤的累計結果。 確認每個回呼都有明確的 return,或使用箭頭函式的隱式回傳(x => x * 2)。
改變原陣列 forEachmapfilterreduce 都不會改變原陣列,但若在回呼內直接改寫 array[i],會產生副作用。 保持函式純粹:不要在高階函式內直接修改來源陣列。
使用 map 只為副作用 map 當作 forEach 使用(只執行 console.log),會浪費記憶體產生不必要的陣列。 需要副作用時直接使用 forEach;需要產生新陣列時才用 map
reduce 初始值遺漏 未提供初始值時,reduce 會把陣列第一個元素當作累計值,當陣列為空會拋錯。 總是提供初始值(如 0[]{}),確保安全性。
findfilter 混用 想要單一結果卻用了 filter,會得到長度為 1 的陣列,需要再取 [0] 直接使用 find,語意更清晰且效能較佳。
someevery 內部副作用 some/every 的回呼裡改變外部變數,可能導致難以追蹤的錯誤。 保持回呼純粹,僅回傳布林值。

最佳實踐

  1. 先思考意圖:是「轉換」(map)、 「篩選」(filter)、 「彙總」(reduce)、還是「搜尋」(find)?
  2. 保持函式純粹:避免在回呼裡改變外部狀態。
  3. 提供初始值:尤其使用 reduce 時,永遠給一個明確的初始值。
  4. 利用鏈式呼叫:多個操作可以串在一起,寫出流暢的資料管線。
// 範例:先篩選、再轉換、最後統計總和
const totalPrice = products
  .filter(p => p.stock > 0)               // 只要有庫存
  .map(p => p.price * p.quantity)         // 計算每項小計
  .reduce((sum, sub) => sum + sub, 0);    // 加總

實際應用場景

場景 典型使用方法 為何適合
表單驗證 every 必須所有欄位都符合驗證規則才送出。
使用者權限檢查 some 只要使用者擁有任一必要權限即可通過。
從 API 取得大量資料,取出關鍵欄位 map 把原始 JSON 轉成簡化的陣列供 UI 使用。
分頁或搜尋結果的即時過濾 filter 依使用者輸入的關鍵字即時篩選清單。
統計圖表的資料彙整 reduce 把每日交易紀錄彙總成月份報表。
找出第一筆符合條件的錯誤日誌 find 只需要第一個符合條件的 log 來定位問題。
遍歷資料寫入資料庫 forEach 每筆資料執行非同步寫入,無需返回值。

範例: 假設你在開發一個電商後台,需要「列出本月銷售額前 5 名的商品」:

// 假設 sales 為本月每筆訂單資料
const topProducts = sales
  .reduce((map, order) => {
    map[order.productId] = (map[order.productId] || 0) + order.amount;
    return map;
  }, {})                                 // 依商品彙總銷售額
  .Object.entries(map)                    // 轉成陣列 [[id, amount], ...]
  .sort((a, b) => b[1] - a[1])             // 依金額降序
  .slice(0, 5)                             // 前 5 名
  .map(([id, amount]) => ({ id, amount })); // 轉回物件陣列

console.table(topProducts);

此範例結合了 reduceObject.entriessortslicemap,展示了高階函式在實務中的威力。


總結

  • forEach 用於副作用,不回傳值;
  • map 產生新陣列,適合資料轉換;
  • filter 用於條件篩選
  • reduce 是最強大的累積/彙總工具,可產生任意型別的結果;
  • find 直接返回第一筆符合條件的元素;
  • someevery 分別檢查任一全部條件。

掌握這七大方法後,你可以以 宣告式可組合 的方式處理各種陣列需求,寫出更簡潔可讀可維護的 JavaScript 程式碼。未來在面對複雜資料流時,記得先抽象出「我要做什麼」的意圖,再選擇最貼切的陣列方法,讓程式碼自然流暢,開發效率倍增。祝你寫程式愉快! 🚀