プログラミング言語は、繰り返し実行するタスクを素早く終わらせるのがとても得意です。基本的な計算処理から、同じような作業がたくさんあるのならどんな状況でもこなします。今度は JavaScript でそういった目的を果たすために使用するループ構造を見てみましょう。

前提条件: 基本的なコンピュータの知識および HTML と CSS への理解、JavaScript の第一歩
目的: JavaScript でループの使い方を理解する。

ループの中にとどまる

ループ、ループ、ループ。 朝食用シリアルや、ジェットコースター音楽でもおなじみですが、プログラミングにおいても、とても重要な概念です。プログラミングにおけるループとは同じことを何度も何度も繰り返すことで、反復繰り返しとも言われます。

それでは、農場主のケースについて考えてみましょう。彼は家族を養うため十分な食料があるか確認しようとしています。それを実現するため、以下のようなループを使用するでしょう。


ループにはたいてい以下のような機能があります。

  • カウンター: ループの開始地点で、初期化される値です。 (上記の絵の、"I have no food" [食料がない] の部分です)。
  • 終了条件: ループが終了する条件です。たいていはカウンターがある値に達した場合に終了します。上記の絵の、"Do I have enough food?" [十分な食料があるか?] の部分です。例えば、家族に食べさせる 10 個の食料が必要である、というようなことです。
  • イテレーター: これは一般的には終了条件を満たすまで、カウンターの値をループごとに少量ずつ増加させます。上記の絵には明示的には描いていませんが、農場主が 1 時間に 2 つの食料を集めることができると考えるとします。この場合、1 時間ごとに 2 つずつ食料が増えていき、農場主は十分な食料が集まったかを確認することができます。もし食料が 10 個になったら (終了条件)、集めるのをやめて家に帰ることができるでしょう。

疑似コードでは、以下のようになるでしょう。

loop(food = 0; foodNeeded = 10) {
  if (food = foodNeeded) {
    exit loop;
    // 十分な食料が集まりました。家に帰りましょう
  } else {
    food += 2; // 1 時間経って 2 つの食料を集めました
    // ループはさらに続きます
  }
}

最初に、必要な食料が 10 に設定され、農場主が現在持っている食料は 0 に設定されます。ループの繰り返しごとに、農場主の持っている食料が必要な食料の数に等しいかを調べています。もしそうであれば、ループを抜けられます。そうでなければ、農場主は 1 時間ごとに 2 つの食料を集めるのを繰り返します。

どうしてこんなことをするの?

これで、恐らくループの背後にあるコンセプトが理解できたことでしょう。けれど、「それが JavaScript のコードを書くのにどう役立つの?」と思っているかもしれませんね。先ほどループは同じことを繰り返すことだと言いいましたが、それは素早く繰り返し同じ作業を完了させるのに最適なことなのです。

たいてい、コードは繰り返しの度に異なることをします。つまり、ループを使用して、似ているけれど全く異なるタスクを完了させられるのです。もし異なる計算を何度もしなければならないとしたら、同じことを何度もするのではなく、異なることをしたいですよね。

ループがどれだけ素晴らしいものかを説明する例を見てみましょう。100 個のランダムな円を <canvas> 要素に描きたいとします (更新ボタンを押して、例を何度となく実行し、結果が異なることを見てみましょう。)

今はコードを全て理解する必要はありません。ですが、コードの一部で 100 個の円を実際に描いている箇所を見てみましょう。

for (var i = 0; i < 100; i++) {
  ctx.beginPath();
  ctx.fillStyle = 'rgba(255,0,0,0.5)';
  ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
  ctx.fill();
}
  • random()はコードの前半で定義され、0からx-1までの整数を返します
  • WIDTHHEIGHTは、内側のブラウザウィンドウの幅と高さです。

基本的な考えがわかりましたか?このコードをループを使用して 100 回実行しますが、毎回ページ内のランダムな場所に円を描いています。必要なコードは 100 個の円を描くときも、1000 個でも 10,000 個でも同じです。1 か所だけ変更すればいいのです。

ここでループを使用しないとすれば、次のコードを描きたい数だけ繰り返し書かなければなりません。

ctx.beginPath();
ctx.fillStyle = 'rgba(255,0,0,0.5)';
ctx.arc(random(WIDTH), random(HEIGHT), random(50), 0, 2 * Math.PI);
ctx.fill();

これはとてもつまらなく、素早くメンテナンスするのが難しいコードです。ループが一番です。

標準的な for ループ

ここからは、具体的なループの構造を見ていきましょう。最初は、特によく使うことになるであろう for ループについてです。構文は以下の通りです。

for (初期化処理; 終了条件; 最後の式) {
  // 実行するコード
}

ここでは...

  1. for キーワードに続き括弧があります。
  2. 括弧の中にはセミコロンで区切られて以下の項目があります。
    1. 初期化処理: これはたいていの場合、繰り返し回数分インクリメントしていく変数の初期化処理となります。この変数をカウンター変数と呼ぶことがあります。
    2. 終了条件: 既に取り上げた通り、これはループが繰り返しをやめるべき条件を定義します。ほとんどの場合は比較演算子を伴って、終了条件を満たしているかを判定します。
    3. 最後の式: これはループの1回が終了する度に評価される (または実行される) コードです。大体、カウンター変数をインクリメント (またはデクリメント) し、終了条件に近づけていきます。
  3. そして中括弧があり、中括弧の中のコードブロックが各ループの繰り返しで実行されます。

それでは実際の例を見て、これらを明確に分かるようにしてみましょう。

var cats = ['ビル', 'ジェフ', 'ピート', 'ビッグルズ', 'ジャスミン'];
var info = '私の猫の名前は、';
var para = document.querySelector('p');

for (var i = 0; i < cats.length; i++) {
  info += cats[i] + '、';
}

para.textContent = info;

これで次の結果が得られます。

このコードはGitHub でも見られます (動くデモもありますよ)。

これは配列の全ての要素に対して、繰り返し何かを実行するループの使用例です。JavaScript ではとてもよく見られるパターンです。

  1. i をイテレータとして、0 から開始します (var i = 0)。
  2. cats 配列の長さより小さくなくなったら終了すると指定されています。これは重要です、終了条件にはループが継続するための条件が示されています。今回は、i < cats.length が真となるため、ループは継続します。
  3. ループの内側では、現在繰り返し対象となる項目 (cats[i] は cats[i に入っているそのときの値] となります) を info 変数に対してカンマとスペースとともに結合しています。つまり...
    1. 初回の実行時には、i = 0 なので cats[0] + ', ' ("ビル、") が info に対して結合されます。
    2. 2回目の実行時には、i = 1 なので cats[1] + ', ' ("ジェフ、") が info に対して結合されます。
    3. このように、ループ内の処理が実行されるたび、1 が i に加算され (i++)、次の処理が開始されます。
  4. icats.length の値と等しくなったときにループは終了し、ブラウザーはループの後に続くコードを実行します。

: 終了条件を i <= cats.length ではなく、i < cats.length としているのは、コンピュータが数値を 1 からではなく、0 から数えるためです。コードでも i0 から始め、i = 4 (配列内の要素の最後のインデックス) となるまで加算していきます。配列内の要素が 5 つなので  cats.length は 5 となりますが、i = 5 とすると、(配列に 5 のインデックスの要素がないので) undefined となってしまいます。なので、cats.length と同じ値まで (i <=) ではなく、i の最大値を 1 減らして cats.length より小さくなる (i <) まで加算しています。

: 終了条件の指定でよくある間違いは「以下」(<=) ではなく、「等しい」(===) を使ってしまうことです。もし、i = 5 となるまでループを実行したければ、終了条件は i <= cats.length と指定しなければなりません。i === 5 と指定した場合、ループは 1 度も実行されずに終了してしまいます。なぜなら、ループの最初では i5 ではないため、そこで終わってしまうからです。

残る小さな問題は、出力された文が完全ではないことです。

私の猫の名前は、ビル、ジェフ、ピート、ビッグルズ、ジャスミン、

ループの最後の結合処理を変更して文の最後が「、」で終わらないようにしたいと思います。まったく問題ありません。ループの中に条件ブロックを挿入して、これに対処しましょう。

for (var i = 0; i < cats.length; i++) {
  if (i === cats.length - 1) {
    info += cats[i] + 'です。';
  } else {
    info += cats[i] + '、';
  }
}

: このコードは GitHub でも見られます (動いているデモもあります)。

重要: for ループ (他のループも同様) では、初期化処理から継続し、必ず終了条件に達するように注意してください。終了条件に達することが出来ない場合、ループは永遠に回り続け、ブラウザーが強制的に停止するか、クラッシュしてしまうでしょう。これは無限ループといいます。

break でループを終了する

すべての繰り返し処理が終了する前にループを終了したいとき、break ステートメントを使用して終了させることができます。前回の記事、switch ステートメントで、入力した値が switch ステートメントの case にマッチしたとき、switch ステートメントを抜け、それ以降のコードを実行するために break ステートメントを使用しました。 

これはループでも同様で、break ステートメントを使用することで即時ループを抜けて、ブラウザーに、続くコードを実行させることが出来ます。

それでは、連絡先 (電話番号を持っている) の配列の中から特定の連絡先を検索してみましょう。まずは HTML です。検索するテキスト入力用の <input> 要素と、検索内容を送信 (submit) する <button> 要素、検索結果を表示する <p> 要素を備えます。

<label for="search">連絡先の名前: </label>
<input id="search" type="text">
<button>検索</button>

<p></p>

現在の JavaScript について:

var contacts = ['クリス:2232322', 'サラ:3453456', 'ビル:7654322', 'メアリー:9998769', 'ダイアン:9384975'];
var para = document.querySelector('p');
var input = document.querySelector('input');
var btn = document.querySelector('button');

btn.addEventListener('click', function() {
  var searchName = input.value;
  input.value = '';
  input.focus();
  for (var i = 0; i < contacts.length; i++) {
    var splitContact = contacts[i].split(':');
    if (splitContact[0] === searchName) {
      para.textContent = splitContact[0] + ' の電話番号は ' + splitContact[1] + ' です。';
      break;
    } else {
      para.textContent = '連絡先が見つかりません。';
    }
  }
});

  1. コードの先頭で、いくつか変数を宣言しています。その中に、連絡先の情報を持った配列があり、各要素は名前と電話番号をコロンで区切った文字列となっています。
  2. 次に、ボタン (btn) にイベントリスナーを設定しています。ボタンが押されたときに検索結果が戻ってくるようになっています。
  3. テキスト入力欄に入力された値を searchName という変数に格納してから、次の検索に備え、入力欄をクリアし、フォーカスを設定しています。
  4. ここからが本題の for ループです。
    1. カウンター変数を 0 から始め、contacts.length より小さくなくなるまで、ループの繰り返しの度に i を 1 でインクリメントしていきます。
    2. ループの内側では、まず現在の連絡先 (contacts[i]) をコロンの文字で分割し、splitContact という配列に格納します。
    3. それから、条件ステートメントを用いて、splitContact[0] (連絡先の名前) が入力された searchName と等しいかを判定します。もし等しければ、連絡先の電話番号を段落 (<p> 要素) に表示し、break を使用してループを終了しています。
  5. もし、連絡先の名前が入力された検索語に一致しなければ、段落に「連絡先が見つかりません。」という文字列を表示し、ループを継続します。

: すべてのソースは GitHub で見ることが出来ます (動いているデモもあります)。

continue でイテレーションをスキップする

continue ステートメントは break と同じような動作をします。けれど、ループを完全に抜けてしまうのではなく、次のイテレーションまで飛ばします。それでは、今度は入力として数値を受け取り、その数以下で整数の平方である値のみを返すという例を見てみましょう。

HTML は基本的に先ほどの例と同様で、1 つのテキストボックスと出力用の段落があります。JavaScript もループ自体を除けばほぼ同じですので、違う部分のみを示します。

var num = input.value;

for (var i = 1; i <= num; i++) {
  var sqRoot = Math.sqrt(i);
  if (Math.floor(sqRoot) !== sqRoot) {
    continue;
  }

  para.textContent += i + ' ';
}

出力結果はこちらです。

  1. 今回の入力内容は数値 (num) です。for ループには、カウンターの初期値として、(今回は 0 ではなく) 1 が与えられ、終了する条件としてカウンターが入力値 (num) より大きくなった場合と指定されており、イテレーターとして、カウンターに 1 ずつ加算するよう指定されています。
  2. ループ内部では、各値の平方根を Math.sqrt(i) を使用して求め、求めた平方根を切り捨てた値が、切り捨てる前の平方根と等しいかどうかを調べています (切り捨てには Math.floor() に任意の数値を渡します)。
  3. もし、平方根と切り捨てた数値が等しくないのなら (!==)、平方根は整数ではないことを示しています。整数以外には興味がありませんので、continue ステートメントを用いて、その数値をどこにも保持することなく、次のループのイテレーションまでスキップします。
  4. もし、その平方根が整数値であるならば、if ブロックは飛ばされるので、continue ステートメントは実行されません。代わりに、現在の i の値を段落の内容の後ろにスペースと一緒に結合します。

: すべてのソースは GitHub でも見ることが出来ます (動いているデモもあります)。

while と do ... while

forはJavaScriptで利用可能な唯一のループのタイプではありません。実際には多くのものがありますが、これらのすべてを理解する必要はありませんが、仕事の同じ機能をわずかに異なる方法で認識できるように、他のものの構造を見ておく価値があります。

まず、whileループを見てみましょう。このループの構文は次のようになります。

initializer
while (exit-condition) {
  // code to run

  final-expression
}

これはforループとよく似ていますが、イニシャライザ変数がループの前に設定され、最後の式は実行するコードの後にループ内に含まれます。 exit-conditionは、括弧の中に含まれています。これは、forキーワードの前にwhileキーワードが付いています。

同じ3つの項目がまだ存在し、forループと同じ順序で定義されています。これは理にかなっています。イニシャライザを定義してexit条件に達したかどうかを確認する必要があります ; ループ内のコードが実行された後(ループが完了した)、final-conditionが実行されます。これは、exit-conditionにまだ達していない場合にのみ発生します。

catリストの例をもう一度見てみましょうが、whileループを使うように書き直してみましょう:

var i = 0;

while (i < cats.length) {
  if (i === cats.length - 1) {
    info += 'and ' + cats[i] + '.';
  } else {
    info += cats[i] + ', ';
  }

  i++;
}

: これは期待どおりに動作します。GitHubでライブ実行してみてください(完全なソースコードを見ることもできます)。

do ... whileループは非常によく似ていますが、while構造にはバリエーションがあります。

initializer
do {
  // code to run

  final-expression
} while (exit-condition)

これはループとよく似ていますが、イニシャライザ数がループの前に設定され、最後の式は実行するコードの後にループ内に含まれます。exit-conditionは、括弧の中に含まれています これは、キーワードの前にはキーワードが付いています。

同じ3つの項目がまだ存在し、ループと同順位で定義されています。これは理にかなっています。イニシャライザを定義して終了条件に達するかどうかを確認する必要があります; ループ内のコードが実行された後(ループが完了した)、最終条件が実行されます。これは、exit-conditionにまだしていない場合にのみ発生します。

猫リストの例をもう一度見てみましょうが、ループを使うように書き直してみましょう:

var i = 0;

do {
  if (i === cats.length - 1) {
    info += 'and ' + cats[i] + '.';
  } else {
    info += cats[i] + ', ';
  }

  i++;
} while (i < cats.length);

: 再度、これは期待どおりに動作します。GitHubでライブ実行してみてください(完全なソースコードを見ることもできます)。

重要: while と do ... while では、すべてのループと同様に、イニシャライザが反復されて最終的に終了条件に達するようにする必要があります。そうしなければループは永遠に進み、ブラウザはそれ強制的に停止させるか、クラッシュします。 これは無限ループと呼ばれます。

アクティブラーニング: カウントダウンを開始します!

この練習では、出力ボックスへの簡単な起動カウントダウンを10からBlast offまで印刷してください。 具体的には、

  • 10から0までのループ。イニシャライザを提供します — var i = 10;.
  • 各繰り返しに対して、新しい段落を作成し、それを出力<div>に追加します。これは、var output = document.querySelector( '。output');を使用して選択したものです。 コメントでは、ループ内のどこかで使用する必要がある3つのコード行を提供しました
    • var para = document.createElement('p'); — 新しいパラグラフを作成します
    • output.appendChild(para); — appends the paragraph to the output <div>.
    • para.textContent = — makes the text inside the paragraph equal to whatever you put on the right hand side, after the equals sign.
  • 反復回数が異なると、その反復の段落に異なるテキストを入れる必要があります(条件文と複数のpara.textContent = 行が必要です)。
    • If the number is 10, print "Countdown 10" to the paragraph.
    • If the number is 0, print "Blast off!" to the paragraph.
    • For any other number, print just the number to the paragraph.
  • イテレータを含めることを忘れないでください! しかし、この例では、各反復後にカウントアップしていますが、アップではないので、i ++は必要ありません。どのように下向きに反復しますか?

間違えた場合は、「リセット」ボタンを使用してこの例をいつでもリセットできます。 あなたが本当に立ち往生したら、 "ソリューションを表示"を押して解決策を見てください。

アクティブラーニング: ゲストリストに記入する

この演習では、配列に格納された名前のリストを取得して、それらをゲストリストに入れることが必要です。 しかし、それはそれほど簡単ではありません - 彼らは貪欲で失礼で、常にすべての食べ物を食べるので、私たちはフィルとローラを入れさせたくありません! 私たちは2つのリストを持っています.1つはゲストのためのもの、もう1つはゲストのためのものです。

具体的には、

  • 0から人員配列の長さに反復するループを作成します。var i = 0;のイニシャライザで始める必要がありますが、どのような終了条件が必要ですか?
  • 各ループ反復中に、現在の配列項目が条件文を使用して "Phil"または "Lola"に等しいかどうかをチェックします。
    • そうである場合は、refusedパラグラフのtextContentの最後に配列項目を連結し、その後にコンマとスペースを続けます
    • そうでない場合は、配列項目を、admitted パラグラフのtextContentの末尾に連結し、その後にコンマとスペースを続けます

私たちはすでにあなたに次のものを提供しました:

  • var i = 0; — Your initializer.
  • refused.textContent += — the beginnings of a line that will concatenate something on to the end of refused.textContent.
  • admitted.textContent += — the beginnings of a line that will concatenate something on to the end of admitted.textContent.

余分なボーナスの質問 - 上のタスクを正常に完了すると、カンマで区切られた2つの名前リストが残されますが、それらは整頓されません。それぞれの末尾にカンマがあります。 それぞれの場合に最後のカンマをスライスする行を記述する方法を理解し、最後に完全停止を追加できますか? 便利な文字列メソッドの記事を見てください。

間違えた場合は、「リセット」ボタンを使用してこの例をいつでもリセットできます。 あなたが本当に立ち往生したら、 "ソリューションを表示"を押して解決策を見てください。

どのタイプのループを使用しますか?

基本的な用途ではfor、while、do ... whileループは主に互換性があります。 それらはすべて同じ問題を解決するために使用することができます。どちらを使用するかは、あなたの個人的な好みに大きく左右されます。これは、覚えやすいか最も直感的です。 それらをもう一度見てみましょう。

まずは for:

for (initializer; exit-condition; final-expression) {
  // code to run
}

while:

initializer
while (exit-condition) {
  // code to run

  final-expression
}

そして最後は do...while:

initializer
do {
  // code to run

  final-expression
} while (exit-condition)

イニシャライザ、exit-condition、final-expressionはすべて括弧内にきちんと入れなければならないので、おそらくすべてを覚えておくのが最も簡単かもしれないので、少なくとも始めることをお勧めします。 あなたがそれらを紛失していないことを確認してください。

: 高度な/特殊な状況やこの記事の範囲を超えて有用な他のループタイプ/機能もあります。 ループ学習をさらに進めたい場合は、高度なループとイテレーションガイドをお読みください。

まとめ

この記事では、背後にある基本的な概念と、JavaScriptでコードをループする際に使用できるさまざまなオプションについて説明しました。今はループが反復コードを処理するための良い仕組みである理由がはっきり分かり、自身の例で使用できることを誇らしく思うでしょう。

あなたが理解できなかったことがあれば、記事をもう一度読んだり、ヘルプを求めて私たちに連絡してください。

関連情報

このモジュール内の文書

 

ドキュメントのタグと貢献者

このページの貢献者: silverskyvicto, hysryt, chameleonhead, mfuji09, Uemmra3
最終更新者: silverskyvicto,