17
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

JavaScriptのforEach内でbreakができない理由【備忘録】

Last updated at Posted at 2025-06-16

背景

社内でJavaScriptの反復処理について話が上がった際に、「forEachbreak を使って抜けることができない」と聞きました。

なんとなくの理解しか持ち合わせていなかったため、この機会に forEach の仕様を見ながらどのような動作をするのか確認してみました。

forEachについて

forEach は配列の各要素に対して、処理を実行するためのメソッドです。
以下のように使うことができます

const favoriteInstruments = ["ドラム", "ギター", "スティールパン"]

favoriteInstruments.forEach((instrument) => {
  console.log(`好きな楽器: ${instrument}`)
})
// 出力例: 
//   好きな楽器: ドラム
//   好きな楽器: ギター
//   好きな楽器: スティールパン

仕様について

for文は構文として定義されていますが、forEach はメソッドです。
ECMA-262forEach の仕様を見てみるとこのようになっています。

1. Let O be ? ToObject(this value).
2. Let len be ? LengthOfArrayLike(O).
3. If IsCallable(callback) is false, throw a TypeError exception.
4. Let k be 0.
5. Repeat, while k < len,
    a. Let Pk be ! ToString(𝔽(k)).
    b. Let kPresent be ? HasProperty(O, Pk).
    c. If kPresent is true, then
        i. Let kValue be ? Get(O, Pk).
        ii. Perform ? Call(callback, thisArg, « kValue, 𝔽(k), O »).
    d. Set k to k + 1.
6. Return undefined.

これを見ると、対象の配列をループさせて forEach 内に書いた関数をコールバック関数として呼び出していることがわかります。

コールバック関数

コールバック関数は別の関数に引数として渡され、その関数の中で後から呼び出される関数です。
「処理の一部を呼び出し元に委ねたい」という時に用いられます。

function greet(name) {
  console.log("こんにちは、" + name + "さん!")
}

// greet をコールバックとして使う関数
function processUserInput(callback) {
  const name = "太郎"
  callback(name) // ここで greet("太郎") が実行される
}

processUserInput(greet)
// 出力: こんにちは、太郎さん!

forEachでbreakが使えない理由

forEach((item) => { ... })の書き方は、書き心地がfor文に似ているため、一見するとループの中に処理を書いているように思えます。

しかし実際には forEach は関数であり、(item) => { ... }の部分は 関数を引数として渡している(=コールバック関数) という形になります。

つまり、以下のように書いているのと本質的には同じです。

function callback(num) {
  console.log(num)
}

const array = [1, 2, 3]
array.forEach(callback) // callback を引数として渡している

このように、forEach の中に書いた処理は「関数の中身」なので、そこで break を使おうとすると、 ループではなく「普通の関数の中で break を使った」 ことになってしまいます。

JavaScriptでは、break はfor文などのループの中でしか使えないため、以下のようなコードはエラーになります。

function callback(item) {
  if (item === 2) {
    break // エラー
  }
  console.log(item)
}

const array = [1, 2, 3]
array.forEach(callback)

また、無名関数を直接 forEach に渡した場合も同じで、関数の中に break を書いていることになるため、やはりエラーになります。

const array = [1, 2, 3]
array.forEach((item) => {
  if (item === 2) {
    break // エラー
  }
  console.log(item)
})

終わりに

今回はECMA-262の仕様から forEach を理解するアプローチをとってみました。
特に、forEach に無名関数を渡す場合は、構文が for 文に似ているため、for 文と同じように書けると誤解しやすいと感じました。

forEach に限らない話ではありますが、仕様を知っていないと思わぬ動作を引き起こす可能性があるため、普段何気なく使っているメソッドや構文についても、「どうしてこのような動作になるのか」という視点で掘り下げることが大切だと思いました。

17
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
17
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?