配列の各要素でループするには、主に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を使うのが一番わかりやすくて良い選択肢でしょう。