アクションを繰り返す必要がよくあります。
例えば、リストから商品を順番に出力したり、1から10までの各数字に対して同じコードを実行したりします。
ループは、同じコードを複数回繰り返す方法です。
「while」ループ
`while`ループの構文は以下のとおりです。
while (condition) {
// code
// so-called "loop body"
}
`condition`が真である間、ループ本体の`code`が実行されます。
例えば、以下のループは`i < 3`の間、`i`を出力します。
let i = 0;
while (i < 3) { // shows 0, then 1, then 2
alert( i );
i++;
}
ループ本体の単一の実行を反復と呼びます。上記の例では、ループは3回反復します。
上記の例で`i++`がなかった場合、ループは(理論上は)永遠に繰り返されます。実際には、ブラウザはこうしたループを停止する方法を提供しており、サーバーサイドのJavaScriptではプロセスを強制終了できます。
比較演算だけでなく、任意の式や変数をループ条件にすることができます。条件は`while`によって評価され、ブール値に変換されます。
例えば、`while (i != 0)`をより短く書く方法は`while (i)`です。
let i = 3;
while (i) { // when i becomes 0, the condition becomes falsy, and the loop stops
alert( i );
i--;
}
ループ本体に1つのステートメントしかない場合は、中括弧`{…}`を省略できます。
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`ループはより複雑ですが、最も一般的に使用されるループでもあります。
これは次のようになります。
for (begin; condition; step) {
// ... loop body ...
}
例を通してこれらの部分の意味を学びましょう。以下のループは、`i`が`0`から`3`未満になるまで`alert(i)`を実行します。
for (let i = 0; i < 3; i++) { // shows 0, then 1, then 2
alert(i);
}
`for`ステートメントを部分ごとに調べましょう。
部分 | ||
---|---|---|
開始 | let i = 0 |
ループに入るときに1回実行されます。 |
条件 | i < 3 |
各ループ反復の前にチェックされます。偽の場合、ループは停止します。 |
本体 | alert(i) |
条件が真である間、繰り返し実行されます。 |
ステップ | i++ |
各反復で本体の後に実行されます。 |
一般的なループアルゴリズムは次のようになります。
Run begin
→ (if condition → run body and run step)
→ (if condition → run body and run step)
→ (if condition → run body and run step)
→ ...
つまり、`begin`は1回実行され、その後反復します。各`condition`テストの後、`body`と`step`が実行されます。
ループが初めての場合は、例に戻って、紙の上でステップバイステップでどのように実行されるかを再現すると役立つかもしれません。
これが私たちのケースで実際に起こることです。
// for (let i = 0; i < 3; i++) alert(i)
// run begin
let i = 0
// if condition → run body and run step
if (i < 3) { alert(i); i++ }
// if condition → run body and run step
if (i < 3) { alert(i); i++ }
// if condition → run body and run step
if (i < 3) { alert(i); i++ }
// ...finish, because now i == 3
ここでは、「カウンター」変数`i`がループ内で直接宣言されています。これは「インライン」変数宣言と呼ばれます。このような変数はループ内でのみ表示されます。
for (let i = 0; i < 3; i++) {
alert(i); // 0, 1, 2
}
alert(i); // error, no such variable
変数を定義する代わりに、既存の変数を使用できます。
let i = 0;
for (i = 0; i < 3; i++) { // use an existing variable
alert(i); // 0, 1, 2
}
alert(i); // 3, visible, because declared outside of the loop
部分のスキップ
`for`のどの部分もスキップできます。
例えば、ループの開始時に何もする必要がない場合は、`begin`を省略できます。
ここに例を示します。
let i = 0; // we have i already declared and assigned
for (; i < 3; i++) { // no need for "begin"
alert( i ); // 0, 1, 2
}
`step`部分も削除できます。
let i = 0;
for (; i < 3;) {
alert( i++ );
}
これにより、ループは`while (i < 3)`と同じになります。
実際にはすべてを削除して、無限ループを作成できます。
for (;;) {
// repeats without limits
}
2つの`for`のセミコロン`;`は必ず存在する必要があることに注意してください。そうでなければ、構文エラーになります。
ループの中断
通常、ループは条件が偽になったときに終了します。
しかし、特別な`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++) {
// if true, skip the remaining part of the body
if (i % 2 == 0) continue;
alert(i); // 1, then 3, 5, 7, 9
}
`i`が偶数の値の場合、`continue`ディレクティブは本体の実行を停止し、`for`の次の反復(次の番号付き)に制御を渡します。そのため、`alert`は奇数の値に対してのみ呼び出されます。
奇数値を表示するループは次のようになります。
for (let i = 0; i < 10; i++) {
if (i % 2) {
alert( i );
}
}
技術的な観点からは、これは上記の例と同じです。確かに、`continue`を使用する代わりに、コードを`if`ブロックでラップするだけで済みます。
しかし、副作用として、ネストレベルが1つ増えました(中括弧内の`alert`呼び出し)。`if`内のコードが数行より長い場合、全体的な可読性が低下する可能性があります。
式ではない構文構造は、三項演算子`?`と一緒に使用できません。特に、`break/continue`などのディレクティブは使用できません。
例えば、このコードを取り上げると
if (i > 5) {
alert(i);
} else {
continue;
}
…疑問符を使用して書き直すと
(i > 5) ? alert(i) : continue; // continue isn't allowed here
…機能しなくなります。構文エラーが発生します。
これは、`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})`, '');
// what if we want to exit from here to Done (below)?
}
}
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 an empty string or canceled, then break out of both loops
if (!input) break outer; // (*)
// do something with the value...
}
}
alert('Done!');
上記のコードでは、`break outer`は`outer`という名前のラベルを上向きに探し、そのループから抜け出します。
そのため、制御は`(*)`から`alert('Done!')`に直接移動します。
ラベルを別の行に移動することもできます。
outer:
for (let i = 0; i < 3; i++) { ... }
`continue`ディレクティブもラベルで使用できます。この場合、コードの実行はラベル付きループの次の反復にジャンプします。
ラベルでは、コード内の任意の場所にジャンプすることはできません。
例えば、これは不可能です。
break label; // jump to the label below (doesn't work)
label: for (...)
`break`ディレクティブはコードブロックの中に存在する必要があります。技術的には、ラベル付きのコードブロックであれば何でも構いません。
label: {
// ...
break label; // works
// ...
}
ただし、上記の例で見たように、`break`は99.9%の確率でループ内で使用されます。
`continue`はループ内でのみ可能です。
まとめ
3種類のループを扱いました。
- `while` - 条件は各反復の前にチェックされます。
- `do..while` - 条件は各反復の後にチェックされます。
- `for (;;)` - 条件は各反復の前にチェックされ、追加の設定が利用可能です。
「無限」ループを作成するには、通常`while(true)`構成が使用されます。このようなループは、他のループと同様に、`break`ディレクティブで停止できます。
現在の反復で何もせずに次の反復に進みたい場合は、`continue`ディレクティブを使用できます。
`break/continue`はループの前にラベルをサポートしています。ラベルは`break/continue`が入れ子のループから外側のループにエスケープするための唯一の方法です。
コメント
<code>
タグを使用し、数行の場合は<pre>
タグで囲み、10行以上の場合にはサンドボックス(plnkr、jsbin、codepen…)を使用してください。