JavaScript | 配列・オブジェクト:ループ処理 – forEach

JavaScript JavaScript
スポンサーリンク

forEach とは何か

forEach は「配列の各要素に対して、指定した関数を順番に“実行する”」ためのメソッドです。ここが重要です:forEach は“副作用(処理をすること)”が目的で、戻り値として新しい配列は返しません。要素を変換して配列を作るなら map、抽出するなら filter を使い、forEach は“ログ記録・集計更新・DOM 操作”のような実行型の処理に向いています。


基本の使い方(副作用を明確に)

全要素に対して処理する

const nums = [10, 20, 30];
nums.forEach(n => {
  console.log(n); // 10, 20, 30
});
// 返り値は undefined(新しい配列は作らない)
JavaScript

ここが重要です:forEach のコールバック内で何を“するか”だけを書き、結果の配列が必要な場面では他メソッドに切り替えます。

要素を使って集計・外部更新

let sum = 0;
const logs = [];
[1, 2, 3].forEach(n => {
  sum += n;               // 集計(外部変数へ)
  logs.push(`+${n}`);     // 結果を別配列へ
});
console.log(sum);  // 6
console.log(logs); // ["+1", "+2", "+3"]
JavaScript

ここが重要です:非破壊を保つため、“対象配列そのもの”は基本的に変更しない。結果は別の変数や配列に入れます。


コールバックの引数と thisArg(使える情報)

value・index・array の3引数

const arr = ["A", "B", "C"];
arr.forEach((value, index, array) => {
  console.log(`${index}/${array.length}: ${value}`);
});
// 0/3: A, 1/3: B, 2/3: C
JavaScript

ここが重要です:位置情報(index)や全体(array)が欲しい処理でも、追加の参照なしで書けます。

thisArg で文脈を渡す(必要時のみ)

const cfg = { prefix: "ID:" };
const ids = [1, 2];
ids.forEach(function (id) {
  console.log(this.prefix + id);
}, cfg);
// ID:1, ID:2
JavaScript

ここが重要です:通常は外部変数の参照(クロージャ)で十分です。this を使う設計の場合のみ thisArg を渡します。


制御フローの注意(深掘りポイント)

break・return で“止められない”

[1, 2, 3].forEach(n => {
  if (n === 2) return; // これは“その要素の関数を終了”するだけ(ループは続く)
  console.log(n);
});
// 出力: 1, 3(2のときはスキップしたが、ループは止まらない)
JavaScript

ここが重要です:forEach には break がありません。途中で“完全に止めたい”なら for…of/for、存在判定なら some/find を使います。

スキップは if ガードで書く

arr.forEach(x => {
  if (x == null) return; // この要素は何もしない(continue 相当)
  // 有効な要素だけ処理
});
JavaScript

例外で止めるのは最終手段

throw すれば止まりますが、通常は意図に合った他メソッドへ切り替えた方が読みやすく安全です。


非同期との組み合わせ(await の落とし穴)

forEach の中で await しても“待たれない”

const ids = [1, 2, 3];
ids.forEach(async id => {
  const res = await fetch(`/api/${id}`);
  // ここで await しても、外側は待たない
});
// 直後に続行してしまう(完了を待たない)
JavaScript

ここが重要です:非同期を順序通りに処理したいなら for…of+await。並列で良いなら map で Promise を作り、Promise.all で待機します。

// 並列
const promises = ids.map(id => fetch(`/api/${id}`).then(r => r.json()));
const results = await Promise.all(promises);

// 逐次
const results2 = [];
for (const id of ids) {
  const r = await fetch(`/api/${id}`); results2.push(await r.json());
}
JavaScript

疎配列・undefined の挙動(穴はスキップされる)

空スロットは“呼ばれない”

const sparse = Array(3); // [ <3 empty items> ]
sparse.forEach((v, i) => console.log(i, v)); // 何も出ない
JavaScript

ここが重要です:forEach は“存在する要素”だけを処理します。穴も処理したいなら Array.from で正規化してから使うと安全です。

const normalized = Array.from(sparse, x => x ?? null);
normalized.forEach((v, i) => console.log(i, v)); // 0 null, 1 null, 2 null
JavaScript

forEach と他メソッドの使い分け(役割を明確に)

map/filter/reduce/for…of との違い

  • 変換(新配列が欲しい):map
  • 抽出(条件で残す):filter
  • 集約(ひとつにまとめる):reduce
  • 柔軟なループ制御(break/continue/await):for…of
  • 副作用の実行(ログ、更新、外部 I/O):forEach

ここが重要です:結果の配列が欲しいのに forEach を使うと、外で push などの副作用が増え、読みづらくなります。“目的に合う道具”を選ぶだけで品質が上がります。


実務レシピ(副作用を丁寧に書く)

ログ出力・検証メッセージの収集

const msgs = [];
input.forEach((v, i) => {
  if (v == null) msgs.push(`index ${i}: missing`);
});
JavaScript

DOM 操作(描画のための副作用)

items.forEach(item => {
  const li = document.createElement("li");
  li.textContent = `${item.name} (${item.price})`;
  list.appendChild(li);
});
JavaScript

集合・辞書への投入

const set = new Set();
const dict = new Map();
rows.forEach(r => {
  set.add(r.tag);
  dict.set(r.id, r);
});
JavaScript

パフォーマンスと設計の指針

ループ中に対象配列を破壊しない

forEach の途中で splice/push をすると、意図しないスキップや重複が起きます。結果は別コンテナへ入れる非破壊パターンに。

前処理で正規化してループを軽く

大小統一、trim、型変換、欠損の埋め・除去を先に済ませると、forEach の中身が短くなり、保守性と速度が上がります。

重い処理を毎回しない工夫

正規表現のコンパイルや Collator の生成はループ外で一度だけ。ループ内では使い回します。


まとめ

forEach は「配列の各要素に対して副作用を実行する」ためのメソッドです。新しい配列は返さず、break/await の扱いに制約がある一方で、読みやすく副作用を集中させる場面に強い。穴(疎配列)は呼ばれずスキップ、index・array を引数で参照可能、thisArg も必要時のみ使える。変換・抽出・集約は map/filter/reduce、柔軟制御は for…of に任せ、forEach は「実行」に徹すると、初心者でも意図通りで壊れにくいループが書けます。

タイトルとURLをコピーしました