繰り返し処理は頻繁に必要になります。
例えば、1つずつリストから商品を出力するような場合や、単に1から10の数値それぞれに同じコードを実行する場合などです。
ループ は複数回同じコードを繰り返す手段です。
“while” ループ
while
ループは次の構文になります:
while (condition) {
// code
// いわゆる "ループ本体" です
}
condition
が true
の間、ループの本体の code
が実行されます。
例えば、以下のループは i < 3
の間、i
を出力します:
let i = 0;
while (i < 3) { // 0, 次に 1, 次に 2 を表示
alert( i );
i++;
}
ループ本体の1回の実行は イテレーション と呼ばれます。上の例のループは3回イテレーションします。
もしも上の例に i++
がない場合、ループは (理論上は) 永遠に繰り返されます。実際には、ブラウザはこのようなループを止める方法を提供しており、サーバサイドJavaScriptではそのプロセスを殺すことができます。
比較に限らず、どんな式や変数もループの条件にすることができます。条件は while
によって評価され、真偽値に変換されます。
たとえば、while (i != 0)
をより短く書く方法としてwhile (i)
があります:
let i = 3;
while (i) { // i が 0 になったとき、条件が偽になり、ループが止まります
alert( i );
i--;
}
ループの本体が単一の文である場合、括弧{…}
を省略することができます:
let i = 3;
while (i) alert(i--);
“do…while” ループ
do..while
構文を使うことで、条件チェックをループ本体の 下に 移動させることができます。:
do {
// loop body
} while (condition);
ループは最初に本体を実行した後、条件をチェックし、条件が真である間、本体の実行を繰り返します。
例:
let i = 0;
do {
alert( i );
i++;
} while (i < 3);
この構文の形式は、条件が真になるかどうかに関わらず、少なくとも1度 はループ本体を実行したい場合にのみ使用されるべきです。通常は他の形式が好まれます: while(…) {…}
“for” ループ
for
ループは最も使われるものの1つです。
このようになります:
for (begin; condition; step) {
// ... loop body ...
}
例でこれらのパーツの意味を学びましょう。下のループは i
が 0
から 3
になるまで(3
は含みません)、 alert(i)
を実行します。:
for (let i = 0; i < 3; i++) { // 0, 次に 1, 次に 2 を表示
alert(i);
}
for
文を部分的に調べてみましょう:
パート | ||
---|---|---|
begin | i = 0 |
ループに入ると1度実行されます。 |
condition | i < 3 |
すべてのループのイテレーションの前にチェックされます。条件が偽の場合、ループを停止します。 |
step | i++ |
各イテレーションで、条件チェックの前に本体の後に実行されます。 |
body | alert(i) |
条件が真の間繰り返し実行されます。 |
一般的なループアルゴリズムは次のように動作します:
begin を実行
→ (if condition → body を実行し step を実行)
→ (if condition → body を実行し step を実行)
→ (if condition → body を実行し step を実行)
→ ...
つまり、begin
を1度実行し、その後、各 condition
の評価の後、body
と step
が実行されるという繰り返しになります。
もしループに慣れていない場合は、上の例に戻って、紙の上でステップ毎にどのように動作するかを再現してみると理解しやすいでしょう。
これが今のケースで正確に起こっていることです:
// for (let i = 0; i < 3; i++) alert(i)
// begin を実行
let i = 0
// if condition → body を実行し step を実行
if (i < 3) { alert(i); i++ }
// if condition → body を実行し step を実行
if (i < 3) { alert(i); i++ }
// if condition → body を実行し step を実行
if (i < 3) { alert(i); i++ }
// ...終わり, 今 i == 3 なので
ここで “カウンタ” 変数 i
はループの中で正しく宣言されます。それは “インライン” 変数宣言と呼ばれます。このような変数はループの中でだけ見えます。
for (let i = 0; i < 3; i++) {
alert(i); // 0, 1, 2
}
alert(i); // エラー, そのような変数はありません
変数を宣言する代わりに、既存のものを使うこともできます:
let i = 0;
for (i = 0; i < 3; i++) { // 既存の変数を使用
alert(i); // 0, 1, 2
}
alert(i); // 3, ループの外で宣言されているので見える
一部分の省略
for
の一部分は省略することができます。
例えば、ループの最初で何もする必要がなければ、begin
を省略できます。
このようになります:
let i = 0; // すでに i を宣言し代入済み
for (; i < 3; i++) { // "begin" 不要
alert( i ); // 0, 1, 2
}
同じように step
パートも除去することができます。:
let i = 0;
for (; i < 3;) {
alert( i++ );
}
ループは while (i < 3)
と同じになりました。
実際にはすべてを除くこともできます。それは無限ループになります:
for (;;) {
// 制限なしで繰り返し
}
for
の2つのセミコロン ;
は必須であることに注意してください。ない場合は構文エラーになります。
ループの終わり
通常、ループは条件が偽になると終了します。
ですが、break
ディレクティブを利用して、いつでも強制的にループを終わらせることができます。
例えば、以下のループはユーザに一連の数字を入力するよう求めますが、数字が入力されなかった場合は “中断” します。:
let sum = 0;
while (true) {
let value = +prompt("Enter a number", '');
if (!value) break; // (*)
sum += value;
}
alert( 'Sum: ' + sum );
もしもユーザが空を入力、もしくは入力をキャンセルした場合、break
ディレクティブは行 (*)
で有効になります。それはループをすぐに停止し、ループ後の最初の行へ制御を渡します。つまり、alert
です。
“無限ループ + 必要に応じた break
” の組み合わせは、ループの最初や最後ではなく、途中や本体の様々な場所で条件をチェックする必要がある状況で最適です。
次のイテレーションに進む
continue
ディレクティブは break
の “軽量版” です。ループ全体はストップしません。その代わりに、現在のイテレーションを停止し、新しいイテレーションのスタートを強制します(条件が真であれば)。
現在のイテレーションが完了し、次へ移動したいときに使います。
以下のループは、奇数値のみを出力するよう continue
を使用しています:
for (let i = 0; i < 10; i++) {
// true の場合、本体の残りのパートをスキップ
if (i % 2 == 0) continue;
alert(i); // 1, 次に 3, 5, 7, 9
}
i
の値が偶数の場合、continue
ディレクティブは本体の実行を停止し、for
の次のイテレーションへ制御を渡します(次の番号で)。したがって、alert
は奇数値に対してのみ実行されます。
continue
を使うと入れ子のレベルを減らせます奇数値を表示するループはこのように書くこともできます:
for (let i = 0; i < 10; i++) {
if (i % 2) {
alert( i );
}
}
技術的な観点からは、これは上の例と同じです。確かに、continue
の代わりに if
ブロックでコードをラップするだけです。
しかし、副作用として括弧のネストが1段深くなります。if
の中のコードが長い場合、全体の可読性が下がる可能性があります。
?
の右側には break/continue
を入れないでください式ではない構文構造は、 三項演算子 ?
の中では使えないことに注意してください。特に、ディレクティブ break/continue
はそこでは許可されません。
例えば、次のようなコードがあるとします:
if (i > 5) {
alert(i);
} else {
continue;
}
…これを、疑問符を使って書き直します:
(i > 5) ? alert(i) : continue; // continue はここでは使えません
…この場合は動作を停止します。コードは構文エラーになります:
これは if
の代わりに疑問符演算子 ?
を使用しない別の理由です。
break/continue のためのラベル
一度に複数のネストしたループから抜け出すことが必要となる場合があります。
例えば、下のコードでは 座標 (i, j)
を (0,0)
から (2,2)
へプロンプトするよう、i
と j
をループします:
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`Value at coords (${i},${j})`, '');
// もしここで終了して下にある Done をしたい場合にはどうすればよいでしょう?
}
}
alert('Done!');
ユーザが入力をキャンセルした場合、処理をストップする方法が必要です。
input
の後の通常の break
は内部ループのみの終了です。それだけでは十分ではありません。ここでラベルが救いの手を差し伸べてくれます。
ラベル は、ループの前のコロンがついた識別子です:
labelName: for (...) {
...
}
ループの中の break <labelName>
文はラベルまで抜け出します:
outer: for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
let input = prompt(`Value at coords (${i},${j})`, '');
// 文字から文字またはキャンセルされた場合、両方のループから抜ける
if (!input) break outer; // (*)
// 値に何かをする処理...
}
}
alert('Done!');
上のコードで、break outer
は outer
と名付けされたラベルを上に探し、そのループを抜けます。
そのため、制御は (*)
から alert('Done!')
にまっすぐに進みます。
ラベルを別の行に移動させることもできます:
outer:
for (let i = 0; i < 3; i++) { ... }
continue
ディレクティブもラベルと一緒に使うことができます。このケースでは、実行はラベル付けされたループの次のイテレーションにジャンプします。
ラベルはコードの任意の場所にジャンプすることはできません。
例えば、このようにすることは出来ません:
break label; // 以下のラベルにジャンプはしません
label: for (...)
break
ディレクティブはコードブロックの中にある必要があります。技術的には任意のラベル付けされたコードブロックであれば機能します。
label: {
// ...
break label; // works
// ...
}
…ですが、break
が利用される 99.9% は上の例で見てきたように、ループの内側です。
continue
はループの内側でのみ利用可能です。
サマリ
3種類のループについて説明しました:
while
– 条件は各イテレーションの前にチェックされます。do..while
– 条件は各イテレーションの後にチェックされます。for (;;)
– 条件は各イテレーションの前にチェックされ、追加の設定ができます。
“無限” ループを作るには、通常は while(true)
構造が使われます。このようなループは他の他のループと同様に break
ディレクティブで停止することができます。
もしも現在のイテレーションで何もしたくなく、次のイテレーションに進みたい場合は、continue
ディレクティブを使います。
break/continue
はループの前のラベルをサポートします。ラベルは、 break/continue
でネストされたループを抜けて外側のループに行くための唯一の方法です。