forループは、同じ処理を繰り返し実行するときにもっともよく使われる文のひとつです。
JavaScriptでは、forキーワード、丸括弧(())で囲まれた_ヘッダー_、そして波括弧({})で囲まれループの_本体_を含むブロックから構成されます。
for (initialization; condition; step) {
// code that is executed repeatedly as long as the condition is
// true
}
初期化ではふつうカウンター変数を用意し、条件ではループを続けるか止めるかを判定し、ステップでは繰り返しのたびにカウンターを増やします。 ヘッダーの各部分はセミコロンで区切ります。
const list = ['a', 'b', 'c'];
for (let i = 0; i < list.length; i++) {
// code that should be executed for each item in the array
}
ヘッダーの3つの部分はすべて省略できます。 ただし、実際に省略することはほとんどありません。 詳しくはMDNのドキュメントを読んでみてください。
forループの入れ子forループは入れ子にできます。たとえば、入れ子になった(多次元の)配列を繰り返し処理する場合です。
ループごとに別のカウンター変数を用意するようにしましょう。
const coords = [
[1, 2],
[4, 7],
[10, -3],
];
for (let i = 0; i < coords.length; i++) {
for (let j = 0; j < coords[i].length; j++) {
// do something with coords[i][j]
}
}
break、continue、ラベルループの本体の中では、breakキーワードを使ってループの実行を完全に止めることができます。
これに対してcontinueキーワードは、現在の繰り返しの実行だけを止め、次の繰り返しへ進みます。
入れ子のループを扱うとき、breakとcontinueは、既定では常にもっとも内側のループに適用されます。
この動作は、ラベルを使うと変えられます。
ラベルは、識別子の名前のあとにコロンを付けたものです。
ループの前に(または上に)置きます。
そのラベルをbreakやcontinueと組み合わせると、その文がどのループに適用されるかを指定できます。
outer: for (let i = 0; i < 10; i++) {
for (let j = 0; j < 10; j++) {
// ...
if (j > 5) {
continue outer;
}
// ...
}
}
非同期コードを扱うときは、カウンター変数を正しく宣言することに気をつける必要があります。
1秒待ってからsetTimeoutでカウンター変数を非同期的に合計する例を見てみましょう。
カウンターをvarで宣言した場合、またはletでヘッダーの外で宣言した場合、コードは期待した結果になりません。
let sum = 0;
for (var i = 1; i < 4; i++) {
setTimeout(function () { sum += i; }, 1000);
}
// eventually sum will be 12 instead of 6 (1+2+3)
let sum = 0;
let i;
for (i = 1; i < 4; i++) {
setTimeout(function () { sum += i; }, 1000);
}
// same here, eventually sum will be 12 instead of 6 (1+2+3)
この2つの例では、iは特定の1回のループの繰り返しにスコープされていません。
合計が計算されるころには、iはすでに最終値の4になっているため、合計は4 + 4 + 4 = 12になります。
この問題は、カウンターをヘッダーの中でletで宣言すると避けられます。
こうすると、繰り返しごとに、その1回のループ本体の実行にだけスコープされた専用の変数let iが作られるという特別な効果があります。
そのため、あとで合計を計算するとき、それぞれのコールバック関数は、まだ正しい値を持っている自分専用の変数iを参照します。
let sum = 0;
for (let i = 1; i < 4; i++) {
setTimeout(function () { sum += i; }, 1000);
}
// eventually sum will be 6 (1+2+3) as expected