for
Baseline Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since 2015年7月.
for 文は、括弧で囲みセミコロンで区切った 3 つの引数と、続いてループ内で実行される文(ふつうはブロック文)から成るループを構成します。
試してみましょう
let str = ""; for (let i = 0; i < 9; i++) { str += i; } console.log(str); // 予想される結果: "012345678" 構文
for (initialization; condition; afterthought) statement initialization省略可-
ループが始まる前に一度だけ評価される(代入式を含む)式または変数宣言。ふつうはカウンター変数を初期化するために使われます。この式では任意で、
varキーワードを用いて新しい変数を宣言することもできます。varで宣言された変数はループ内のローカル変数にはなりません。すなわち、forループが属するスコープと同じスコープになります。letで宣言された変数は文内のローカル変数になります。この式の結果は捨て去られます。
condition省略可-
ループのそれぞれの反復処理が行われる前に評価される式です。この式が true と評価された場合は、
statementが実行されます。この式が false と評価された場合は、実行はfor構造に続く最初の式に飛びます。この条件テストはオプションです。省略した場合、この条件は常に true と評価されます。
afterthought省略可-
ループのそれぞれの反復処理の最後に評価される式です。これは、次の
conditionの評価前に行われます。一般的には、カウンター変数を更新または増加するために使われます。 statement-
条件が true と評価された場合に限り実行される文です。ループ内で複数の文を実行するには、ブロック文を使用して文をグループ化してください。ループ内で文を実行しないようにするには、空文 (
;) を使用してください。
解説
他のループ文と同様に、statement 内部でフロー制御文が使用できます。
例
>for の使用
次の for 文は、変数 i を宣言し、それを 0 に初期化することから始まります。i が 9 より小さいことをチェックし、続く 2 つの文を実行し、ループを通過した後ごとに i を 1 増加します。
for (let i = 0; i < 9; i++) { console.log(i); // その他の文 } 初期化ブロックの構文
初期化ブロックは、式と変数宣言の両方を受け入れることができます。ただし、式には括弧で囲んでいない in 演算子を使用することができません。for...in ループと曖昧になるためです。
for (let i = "start" in window ? window.start : 0; i < 9; i++) { console.log(i); } // SyntaxError: 'for-in' loop variable declaration may not have an initializer. // Parenthesize the whole initializer for (let i = ("start" in window ? window.start : 0); i < 9; i++) { console.log(i); } // Parenthesize the `in` expression for (let i = ("start" in window) ? window.start : 0; i < 9; i++) { console.log(i); } 省略可能な for の式
for ループの先頭にある 3 つの式は、省略可能です。例えば、initialization ブロックで変数を初期化する必要はありません。
let i = 0; for (; i < 9; i++) { console.log(i); // その他の文 } initialization ブロックと同様に、condition ブロックも省略可能です。この式を省略した場合は、本体の中でループを脱出できるようにして、無限ループにならないようにしなければなりません。
for (let i = 0; ; i++) { console.log(i); if (i > 3) break; // その他の文 } 3 つのブロックをすべて省略することもできます。繰り返しますが、 break 文を使用してループを終了させ、また break 文の条件がある時点で true になるように、変数を変更(増加)させていることを確認してください。
let i = 0; for (;;) { if (i > 3) break; console.log(i); i++; } しかし、3 つの位置の式をすべて使用する訳ではない場合、特に最初の式で変数を宣言せず、上位のスコープで何かを変更している場合は、代わりに while ループを使用することを検討したほうが意図がより明確になります。
let i = 0; while (i <= 3) { console.log(i); i++; } 初期化ブロックの字句の宣言
初期化ブロック内で変数を宣言する場合、上位のスコープで宣言する場合と異なる点があり、特にループ本体内でクロージャを作成する場合は重要です。例えば、下記のコードを見てください。
for (let i = 0; i < 3; i++) { setTimeout(() => { console.log(i); }, 1000); } 期待通り、0、1、2 とログ出力します。しかし、その変数が上位スコープで定義されている場合は、
let i = 0; for (; i < 3; i++) { setTimeout(() => { console.log(i); }, 1000); } 3、3、3 とログ出力します。なぜかと言うと、それぞれの setTimeout が i 変数を閉じる新しいクロージャを作成しますが、i がループ本体のスコープでない場合、すべてのクロージャは最終的に呼び出されたときに同じ変数を参照します。そして setTimeout() の非同期であるため、すでにループが終了した後に実行され、すべてのキューのコールバック本体の i は 3 という値になります。
これは、初期化に var 文を使用した場合にも起こります。var で宣言された変数は関数スコープのみで、レキシカルスコープにならない(つまり、ループ本体のスコープにすることはできない)からです。
for (var i = 0; i < 3; i++) { setTimeout(() => { console.log(i); }, 1000); } // 3, 3, 3 とログ出力 初期化ブロックのスコープ効果は、宣言がループ本体の中で行われ、たまたま condition と afterthought の部分でアクセス可能であるかのように理解することができます。より正確には、let宣言は for ループに特化されます。もし initialization が let 宣言であれば、ループ本体が評価された後、以下のことが毎回行われます。
- 新しい字句スコープが作成され、新しい
letが宣言された変数が追加されます。 - 前回の反復処理でバインドされた値を用いて、新しい変数を再初期化します。
afterthoughtが新しいスコープで評価されます。
そのため、afterthought 内で新しい変数を割り当てても、前回反復処理したときのバインディングには影響しません。
initialization の後、condition が初めて評価される直前に、新たな字句スコープも生成されます。これらの詳細はクロージャを作成することで観察可能であり、特定の時点でのバインディングを保持できます。例えば、このコードでは initialization 部分で生成されたクロージャは、afterthought 内での i の再代入によって更新されません。
for (let i = 0, getI = () => i; i < 3; i++) { console.log(getI()); } // ログ出力 0, 0, 0 これは、ループ本体の中で getI を宣言した場合のように "0, 1, 2" をログ出力するわけではありません。これは、getI が反復処理するたびに再評価されるのではなく、関数が一度作成され、(ループが最初に初期化されたときに宣言された変数を参照する)変数 i が閉じられるからです。その後、i の値を更新すると、実際には i という新しい変数が作成されますが、getI はそれを見ることができません。これを修正するには、i が更新されるたびに getI を再計算するようにします。
for (let i = 0, getI = () => i; i < 3; i++, getI = () => i) { console.log(getI()); } // ログ出力 0, 1, 2 initialization 内の変数 i は、最初の反復を含む各反復内の i 変数とは別個の存在です。したがって、この例では、反復内の i の値が事前にインクリメントされているにもかかわらず、getI は 0 を返します。
for (let i = 0, getI = () => i; i < 3; ) { i++; console.log(getI()); } // ログ出力 0, 0, 0 実際、変数 i の最初のバインディングをキャプチャして、後でそれを割り当てることができます。この更新された値は、次の新しい i のバインディングを見るループ本体には見えません。
for ( let i = 0, getI = () => i, incrementI = () => i++; getI() < 3; incrementI() ) { console.log(i); } // ログ出力 0, 0, 0 これは "0, 0, 0" とログ出力します。なぜなら、各ループ評価における i 変数は実際には別個の変数ですが、getI と incrementI はどちらも i の初期バインディングを読み書きし、その後に宣言されたものには対応しないからです。
文を持たない for の使用
以下の for の繰り返しでは、 final-expression 句の中でにおけるノードのオフセット位置を検索しています。 statement 節を使用する必要がない場合は、代わりにセミコロンを使用してください。
function showOffsetPos(id) { let left = 0; let top = 0; for ( let itNode = document.getElementById(id); // 初期化 itNode; // 条件式 left += itNode.offsetLeft, top += itNode.offsetTop, itNode = itNode.offsetParent // 更新式 ); // セミコロン console.log( `Offset position of "${id}" element: left: ${left}px; top: ${top}px;`, ); } showOffsetPos("content"); // 出力結果: // Offset position of "content" element: // left: 0px; // top: 153px; for 文の後のセミコロンは必須であることに注意してください。これは空文としての役割を果たすからです。そうしないと、for 文は以下の console.log 行を statement 節として取得し、log を複数回実行させることになる。
2 つの反復用変数の使用
カンマ演算子を用いて、for ループで同時に更新される 2 つのカウンターを作成することができます。複数の let や var の宣言をカンマで結合することもできます。
const arr = [1, 2, 3, 4, 5, 6]; for (let l = 0, r = arr.length - 1; l < r; l++, r--) { console.log(arr[l], arr[r]); } // 1 6 // 2 5 // 3 4 仕様書
| Specification |
|---|
| ECMAScript® 2026 Language Specification> # sec-for-statement> |