Q1条件が成り立っている状態で while に入り、条件に使う値を本体で一度も変えないとどうなりますか?
条件で繰り返す — while / do...while / break / continue
JavaScript の while と do...while を基礎から学びます。回数を先に決められない繰り返しの書き方、無限ループの避け方、continue と break による中断の使い分けを扱います。
空きが出た予約枠に待機列から繰り上げる、残高が足りる間だけ買い足す、といった処理は、終わる条件は決まっていても繰り返す回数を先に数えられません。
この記事では、条件で繰り返す while と do...while、周を飛ばす continue、ループを抜ける break を扱います。
回数を決めずに繰り返す — while と条件の更新
予約に空きが出たとき、待機列の先頭から順に繰り上げたいとします。1 人繰り上げるたびに空き席が 1 つ減り、席が 0 になった時点で止めます。止めるきっかけは周回数ではなく「空きがある間」という状態なので、件数を数えながら回す for では素直に表せません。
while(丸括弧の条件が成り立つ間、波括弧の本体を繰り返す構文)は、丸括弧に条件だけを書きます。条件は 1 周ごとに、本体へ入る前に判定されます。
初期化はループの前に、条件に使う値の更新は本体の中に、それぞれ自分で書きます。
const waitingList = ["山田", "佐藤", "鈴木"]; // 待機列
let openSeats = 2; // 空いている席
// 空き席がある間だけ繰り上げる
while (openSeats > 0) {
const name = waitingList.shift();
openSeats--; // openSeats = openSeats - 1 と同じ意味
console.log(`${name} さんを繰り上げ(残り ${openSeats} 席)`);
}
// 山田 さんを繰り上げ(残り 1 席)
// 佐藤 さんを繰り上げ(残り 0 席)
console.log(`繰り上げ後の待機: ${waitingList.length} 人`); // 繰り上げ後の待機: 1 人
for は更新の式を書く場所が丸括弧の中に決まっていますが、while では本体のどこに書くかを自分で決めます。書く場所を選べる代わりに、更新を書き忘れても文法としては正しいコードのままです。
更新を書き忘れると実行が止まりません
条件が成り立たなくなる周が来なければ、本体は永久に繰り返されます。openSeats-- を書き忘れると、残り席は 2 のままで判定が変わりません。コンソールは 15 秒でタイムアウトを表示しますが繰り返しは止まらず、ページを読み込み直すまで他の演習も動かなくなります。条件に使う値は、本体のどこかで必ず動かします。
先に 1 回実行する — do...while の判定順
通知の送信に失敗したときは、成功するまで何回か送り直します。送信そのものは結果を見る前に 1 回は行う必要がありますが、while は本体に入る前に条件を判定するため、条件に使う変数の初期値しだいでは 1 回も送らずに終わります。
do...while(本体を 1 回実行してから条件を判定する構文)は do { 本体 } while (条件); と書き、末尾のセミコロンまでで 1 つの文です。判定が本体より後に来るので、条件が最初から成り立たない場合でも本体は 1 回実行されます。
const attempts = [false, false, true]; // 送信結果を先に用意した(true が成功)
let sent = true; // 前の通知の結果が true のまま残っている
let tried = 0;
// while は先に判定するので、1 回も送らずに終わる
while (!sent && tried < attempts.length) {
sent = attempts[tried];
tried++;
}
console.log(`while で送った回数: ${tried}`); // while で送った回数: 0
// do...while は判定の前に 1 回目を送り、その結果で続けるかを決める
do {
sent = attempts[tried];
tried++;
console.log(`${tried} 回目の送信: ${sent ? "成功" : "失敗"}`);
} while (!sent && tried < attempts.length);
// 1 回目の送信: 失敗 / 2 回目の送信: 失敗 / 3 回目の送信: 成功
sent が true のまま始めた結果です。while は判定だけで終わり、do...while は 1 回目を送ってから続けるかを決めます。本体を先に実行するのは 1 周目だけで、2 周目からは while と同じく条件が成り立つ間だけ繰り返します。条件が成り立たないときは 1 回も実行したくないなら while、結果を見る前に 1 回は実行したいなら do...while を選びます。
その周だけ飛ばす — continue の使いどころ
出荷リストを上から処理するとき、在庫が無い行は出荷の処理を飛ばして次の行に進みたくなります。飛ばしたい条件が増えるほど if の波括弧が深くなり、本来やりたい出荷の処理が奥に埋もれていきます。
continue(その周の残りの処理を飛ばして次の周へ進む文)を使うと、飛ばしたい条件に当てはまった時点で周を切り上げられます。ループ自体は続くので、次の周は条件の判定から通常どおり始まります。
const names = ["A5 手帳", "デスクライト", "USB ハブ"];
const stocks = [12, 0, 5]; // 0 は欠品
let index = 0;
while (index < names.length) {
const name = names[index];
const stock = stocks[index];
index++; // continue で飛ばされないよう先に進めておく
// 欠品の行は、この周の残りを飛ばして次の周へ
if (stock === 0) {
console.log(`${name}: 欠品のためスキップ`);
continue;
}
console.log(`${name}: ${stock} 個を出荷`);
}
// A5 手帳: 12 個を出荷
// デスクライト: 欠品のためスキップ
// USB ハブ: 5 個を出荷
| 処理する行 | stock の値と判定 | その周の結果 |
|---|---|---|
| 1 行目 A5 手帳 | stock は 12 で 0 ではない | 12 個を出荷と表示 |
| 2 行目 デスクライト | stock は 0 なので continue | 出荷の行は実行されない |
| 3 行目 USB ハブ | stock は 5 で 0 ではない | 5 個を出荷と表示 |
continue が飛ばすのは、その位置から本体の終わりまでの行だけです。continue は for や for...of の本体でも使え、for の丸括弧に書いた更新の式は本体の外にあるので、continue した周でも実行されます。
continue は更新の行も飛ばします
index++ を continue より後ろに書くと、飛ばされた周では index が進みません。次の周も同じ行を読み、stock が 0 のまま条件も変わらないので、繰り返しが終わらなくなります。条件に使う値の更新は continue より前に置きます。
見つかった時点で止める — break の抜け先
ログを上から見て、最初に出たエラーだけを知りたいとします。見つけたあとも最後まで読み続けると処理が無駄になり、書き方によっては後ろにある別のエラーで firstError が上書きされます。
break(実行中のループをその場で終わらせる文)を使うと、条件を満たした周でループを抜けられます。条件分岐の記事で switch を抜けるときに使った文と同じです。見つけた値はループの外で使うので、firstError はループより前に宣言しておきます。
const logs = ["INFO 起動", "WARN 遅延", "ERROR 決済失敗", "ERROR 再送失敗"];
let index = 0;
let firstError = "";
while (index < logs.length) {
const line = logs[index];
index++;
if (line.startsWith("ERROR")) {
firstError = line;
break; // 最初の 1 件が見つかったので、残りは調べない
}
}
console.log(`最初のエラー: ${firstError}`); // 最初のエラー: ERROR 決済失敗
console.log(`調べた行数: ${index}`); // 調べた行数: 3
const line = logs[index]— その周で調べる 1 行breakのあとは波括弧を出て、次のconsole.logの行へ進む
firstError = line— 見つけた行を控えるbreak— この波括弧ではなく、外側の while を抜ける- この下に行を足しても実行されない
break の下にある本体の行も、残りの周も実行されません。抜けた先は while の波括弧の次の行です。break した周でも、その前に書いた index++ は実行済みです。そのため index は 3 で止まり、ループのあとに表示した「調べた行数: 3」は、見つかった行を 1 から数えた位置と一致します。
理解度チェック
まずは1問ずつ答えてみましょう。
Q2条件が最初から成り立たないとき、do...while の本体は何回実行されますか?
Q3ループの中で continue を実行すると、次に何が起きますか?