1. ホーム
  2. JavaScript

【JavaScript】forEach の使い方|配列の各要素に順番に処理を行う

Share

配列の中身を1つずつ取り出して同じ処理を繰り返したい。そんなときに使えるのが Array.prototype.forEach です。for 文のようにカウンタ変数を用意しなくても、配列の各要素に対して順番にコールバック関数を実行できます。この記事では、forEach の基本構文から、コールバックが受け取る3つの引数(要素・インデックス・元配列)、for 文や for...of との違い、似ている map との違い、そして break で途中終了できないという注意点まで、コーディング初心者〜中級者の方に向けて MDN の仕様に沿って解説します。

forEach とは|配列の各要素に順番に処理を行うメソッド

forEach は、配列が持っているメソッドのひとつで、配列の各要素に対して、渡した関数(コールバック関数)を1回ずつ呼び出します。先頭の要素から末尾の要素まで、昇順に1つずつ処理していくのが特徴です。for 文のように i のようなカウンタ変数を自分で増やしていく必要がなく、「配列の中身に対して何かをする」という意図をそのまま書けるため、コードが読みやすくなります。

ひとつだけ先に押さえておきたいのが、forEach の戻り値は常に undefined だという点です。forEach は「各要素に処理を行う(副作用を起こす)」ことが目的のメソッドで、新しい配列を作って返すわけではありません。この性質は後ほど map との違いのところで詳しく説明します。

forEach の基本構文と最小のコード例

基本の書き方はとてもシンプルです。配列のうしろに .forEach() を付け、その引数に「各要素に対して行いたい処理」を関数として渡します。下のように、コールバック関数の第1引数で現在の要素を受け取れます。

script.js
const fruits = ['りんご', 'みかん', 'バナナ'];

// 各要素を1つずつ受け取って処理する
fruits.forEach((fruit) => {
  console.log(fruit);
});

// 出力:
// りんご
// みかん
// バナナ

この例では fruits という配列に対して forEach を呼び出しています。コールバック関数の引数 fruit には、配列の先頭から順に 'りんご''みかん''バナナ' が入り、そのたびに console.log が実行されます。for 文と違ってカウンタ変数や要素数の取得が不要なので、やりたい処理だけに集中して書けるのが分かると思います。

コールバックの3つの引数(要素・インデックス・元配列)

forEach に渡すコールバック関数は、最大で3つの引数を受け取れます。順番に「現在処理している要素」「その要素のインデックス(0 から始まる番号)」「forEach を呼び出した元の配列」です。必要な引数だけを書けばよく、要素しか使わないなら第1引数だけ書けば十分です。

script.js
const scores = [80, 95, 60];

// element(要素), index(番号), array(元の配列)
scores.forEach((element, index, array) => {
  console.log(`${index}番目: ${element}点(全${array.length}件)`);
});

// 出力:
// 0番目: 80点(全3件)
// 1番目: 95点(全3件)
// 2番目: 60点(全3件)

第2引数の index は 0 から始まる連番です。「何番目の要素か」を表示したり、奇数番目だけ処理を変えたりするときに使います。第3引数の array には forEach を呼んだ元の配列そのものが入るので、ループの中で他の要素を参照したいときに役立ちますが、使う場面はそれほど多くありません。それぞれの引数の役割を整理すると次のとおりです。

引数役割
第1引数(element現在処理している要素の値。最もよく使う
第2引数(indexその要素のインデックス。0 から始まる番号
第3引数(arrayforEach を呼び出した元の配列

なお、これらの引数の名前は自由に決められます。elementindex という名前でなくても、itemi のように分かりやすい名前を付けて構いません。大切なのは順番で、1つ目が要素、2つ目がインデックス、3つ目が元配列という並びは固定です。

for 文や for…of との違い

同じ「配列を繰り返す」処理でも、for 文・for...offorEach にはそれぞれ向き不向きがあります。for 文はカウンタ変数を自分で管理する古くからある書き方で、インデックスを細かく操作したいときや、途中で抜けたい・飛ばしたいときに自由が利きます。for...of は配列などの反復可能オブジェクトから要素を順に取り出す構文で、forEach に近い読みやすさを持ちつつ、breakcontinue が使えるのが特徴です。

これに対して forEach は、コールバック関数を各要素に適用するメソッドです。書き方が簡潔で、コールバック内で要素とインデックスを同時に受け取れる手軽さがありますが、後述するように breakcontinue による制御はできません。3つの違いを整理すると次のようになります。

構文特徴
forカウンタ変数を自分で管理する。breakcontinue が使え、最も柔軟
for...of要素を順に取り出す。読みやすく、breakcontinue も使える
forEach各要素にコールバックを適用。簡潔だが breakcontinue は使えない

単純に「全要素を1つずつ処理したい」だけなら forEach が簡潔で読みやすく、途中で抜ける可能性があるなら for...offor 文が向いている、と覚えておくとよいでしょう。

map との違い|forEach は新しい配列を作らない

forEach とよく比較されるのが map です。どちらも配列の各要素に対してコールバックを実行する点は同じですが、戻り値の扱いが決定的に違います。forEach の戻り値は常に undefined で、新しい配列は作りません。一方 map は、コールバックが返した値を集めて新しい配列を作って返します

script.js
const numbers = [1, 2, 3];

// forEach は undefined を返す(新しい配列は作らない)
const result1 = numbers.forEach((n) => n * 2);
console.log(result1); // undefined

// map は各要素を変換した新しい配列を返す
const result2 = numbers.map((n) => n * 2);
console.log(result2); // [2, 4, 6]
console.log(numbers); // [1, 2, 3](元の配列は変わらない)

使い分けの基準はシンプルです。各要素をもとに新しい配列を作りたい(値を変換したい)なら map、配列を作る必要はなく各要素に対して何かを実行したいだけ(ログ出力、DOM 操作、外部への副作用など)なら forEach を使います。「変換した配列が欲しいのに forEach を使ってしまう」のはよくある取り違えなので、戻り値が必要かどうかで判断するとよいでしょう。

forEach は途中で抜けられない(break・continue が使えない)

forEach の重要な制約として、break でループを途中終了したり、continue で次の要素へスキップしたりすることができません。これらは for 文や for...of のような構文の中でしか使えず、forEach のコールバックは普通の関数なので、その中に break を書くと構文エラーになります。return を書くこともできますが、これはそのコールバック1回分の処理を終えるだけで、ループ全体は止まりません(残りの要素の処理は続きます)。

では「条件を満たしたら処理を打ち切りたい」ときはどうするか。素直なのは for...of を使って break で抜ける方法です。あるいは、配列メソッドのまま書きたいなら someevery が使えます。some はコールバックが true を返した時点で繰り返しを止めるため、「見つかったら止める」用途に向いています。

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

// for...of なら break で途中終了できる
for (const n of numbers) {
  if (n > 3) break; // 3 を超えたら抜ける
  console.log(n); // 1, 2, 3
}

// some は true を返した時点で繰り返しを止める
numbers.some((n) => {
  if (n > 3) return true; // ここで繰り返しが止まる
  console.log(n); // 1, 2, 3
  return false;
});

逆に言えば、「最後まで全要素を必ず処理する」と分かっている場面では forEach がそのまま使えます。途中で抜ける必要が出てきたら for...ofsome へ切り替える、という判断ができると、メソッドの選び方で迷わなくなります。

NodeList(querySelectorAll)に forEach を使う

DOM を扱うときによく出てくるのが document.querySelectorAll です。これが返すのは配列ではなく NodeList というオブジェクトですが、NodeList 自身も forEach メソッドを持っているため、配列と同じように各ノードに対して順番に処理できます。取得した複数の要素にまとめてイベントを登録したりクラスを付けたりするときに便利です。

script.js
// .item を持つ要素をすべて取得(戻り値は NodeList)
const items = document.querySelectorAll('.item');

// NodeList も forEach が使える
items.forEach((el, index) => {
  el.textContent = `${index + 1} 番目の項目`;
  el.classList.add('is-ready');
});

ここで注意したいのは、NodeList はあくまで配列「のようなもの」であって、本物の配列ではないという点です。forEach は使えますが、mapfilter といった配列メソッドは NodeList には用意されていません。それらを使いたいときは Array.from(items)[...items] で一度本物の配列に変換してから扱います。また、getElementsByClassName などが返す HTMLCollection には forEach がないため、その場合も配列へ変換してから forEach を使う必要があります。

まとめ

forEach は、配列の各要素に対してコールバック関数を先頭から順に1回ずつ実行するメソッドです。コールバックは要素・インデックス・元配列の3つの引数を受け取れますが、よく使うのは第1引数の要素です。戻り値は常に undefined で新しい配列は作らないため、値を変換した配列が欲しいときは map を使い、各要素に処理を行いたいだけのときに forEach を使う、と覚えておくと迷いません。また forEachbreakcontinue で途中終了できないので、条件次第で抜けたいときは for...ofsomeevery を選びます。querySelectorAll が返す NodeList でもそのまま forEach が使えるので、DOM 操作でも活躍します。

参考ページ