配列の各要素でループするには、主にforof
文とforEach
メソッドを使う方法があります。
では、これらの方法は何が違うのでしょうか。
結論から言うと、どちらもできることはほぼ変わりません。
しかし、ちょっと違う点もあります。
この記事では以下の話を扱いません。
- 通常の
for
文 - 実行速度やパフォーマンス
forEach
forEach
は配列のメソッドの一つで、配列の各要素でループするのに使われます。
例えば、以下のように配列の各要素を出力できます。
const animals = ["dog", "cat", "rabbit"]
// 引数にアロー関数を入れて呼び出す
animals.forEach(animal => { // animalには要素("dog"とか)が入る
console.log(`Animal: ${animal}`)
})
// Animal: dog
// Animal: cat
// Animal: rabbit
できること
インデックスが使える
インデックスを使うループの場合、forEach
のほうが簡単に実装できます。
これはforEach
が取る関数の第二引数がインデックスになるからです。
例えば以下のようにすると、配列の中身をインデックスとともに出力することができます。
const animals = ["dog", "cat", "rabbit"]
animals.forEach((animal, index) => { // indexにはインデックス(0始まり)が入る
console.log(`Animal ${index}: ${animal}`)
})
// Animal 0: dog
// Animal 1: cat
// Animal 2: rabbit
forof
でインデックスを使う場合は、配列のentries
メソッドを使う必要があります(後述)。
できないこと
制御文が使えない
forof
文ではbreak
やcontinue
などの制御文が使えますが、forEach
はただの関数なので使えません。
const animals = ["dog", "cat", "rabbit"]
animals.forEach(animal => {
console.log(`Animal ${index}: ${animal}`)
break // Syntax Error!
})
その代わり、forEach
は関数を取るのでreturn
が使えます。
forEach
でのreturn
は、forof
内で使うcontinue
と同じことができます。
const animals = ["dog", "cat", "rabbit"]
animals.forEach((animal, index) => {
if (index === 1) return // indexが1なら出力しない
console.log(`Animal ${index}: ${animal}`)
})
// Animal 0: dog
// Animal 2: rabbit
ループを中断できない
forEach
のループは途中で中断できません。
forof
文ではbreak
を使うと中断できますが、forEach
では関数内で例外を投げる必要があります。
[1, 2, 5].forEach(x => {
if (x % 2 === 0) throw x // 偶数がひとつでも見つかったら中断
console.log(x)
})
// 1
// Uncaught 2
例外を投げる場合、その例外がどこまで影響を与えるのかを考える必要があります。
上のコードのようにforEach
の呼び出しをtry-catch
で囲んでいない場合、想定外のところまでエラーが伝播するかもしれません。
これはforof
文でも`同じで、途中でエラーを投げるとループを中断できます。
しかしforof
にはその役割を果たすbreak
があります。
そのためループを中断するためだけにエラーを投げることはないです。
forof
forof
文は反復可能オブジェクトをループする構文です。
const array = [1, 4, 5]
// for (const 要素を入れる変数名 of 反復可能オブジェクト) { 処理 }
for (const num of array) {
console.log(num)
}
反復可能オブジェクトには例えば配列(Array
)がありますが、それ以外にもいくつかあります。
また、反復可能オブジェクトは自分で作ることもできます。(詳細)
できること
制御文が使える
forof
文ではbreak
やcontinue
が使えます。
それぞれの文の動作は大体こんな感じです。
-
break
: ループ全体を中断する -
continue
: 次のループに行く
for (const num of [1, 4, 5] {
// 偶数だったら次のループに行く
if (num % 2 === 0) continue
console.log(num)
}
// 1
// 5
関数の戻り値が決められる
関数内で使う場合、その関数の戻り値を決めるためにreturn
が使えます。
例えば、以下の動作をするsumEven
関数を楽に定義できます。
- 配列の要素が偶数の間合計し続ける
- 奇数があったらその時点の合計を返す
function sumEven(array) {
let sum = 0
for (const num of array) {
if (num % 2 !== 0) return sum // 奇数の場合
sum += num
}
return sum // 全ての数が偶数だった場合
}
これをforEach
で実装する場合、一手間必要になります。
一番簡単なのはフラグを用意する方法でしょうか。
function sumEven(array) {
let sum = 0
let flag = false // 奇数の値がすでに出現したかどうか
array.forEach(num => {
if (flag) return // フラグがtrueなら何も足さない
if (num % 2 !== 0) { // 奇数の場合
flag = true
return
}
sum += num
})
return sum // 全ての数が偶数だった場合
}
できないこと
インデックスを使うには一手間必要
forof
文でインデックスを用いたループをしたい場合、配列のentries
メソッドを使う必要があります。
const array = ['A', 'B', 'C']
// (const [インデックスの変数名, 値の変数名] of 配列名.entries())
for (const [index, value] of array.entries()) {
console.log(`No. ${index}: ${value}`)
}
// No. 0: A
// No. 1: B
// No. 2: C
配列のentries
メソッドは、[インデックス, 値]
という形式の値を持つ反復可能オブジェクトを返します。
詳細はMDNをご覧ください。
制御文とforEach
return
とcontinue
forEach
のreturn
、forof
のcontinue
は、実質的に同じように動きます。
-
forEach
のreturn
: 今の関数の実行を終わり、次のループに行く -
forof
のcontinue
: 今回のループを終わり、次のループに行く
それ以外
それ以外の制御文は、forEach
に対応するものはありません。
-
forof
のbreak
:forEach
ではできない - 関数内での
forof
のreturn
:forEach
ではできない
forEach
でbreak
したくなった場合、代わりにforof
を使うのが一番わかりやすくて良い選択肢でしょう。