We're looking for a user researcher to understand the needs of developers and designers. Is this you or someone you know? Check out the post: https://mzl.la/2IGzdXS

この記事は翻訳作業中です。

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

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

ループの中にいさせて

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

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


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

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

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

ループ(食料 = 0; 必要な食料 = 10) {
  if (食料 = 必要な食料) {
    ループを終了;
    // 十分な食料が集まりました。家に帰りましょう
  } else {
    食料 += 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();
}

基本的な考えがわかりましたか?このコードをループを使用して 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>

Now on to the 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 を使ってループを抜ける

The continue statement works in a similar manner to break, but instead of breaking out of the loop entirely, it skips to the next iteration of the loop. Let's look at another example that takes a number as an input, and returns only the numbers that are squares of integers (whole numbers).

The HTML is basically the same as the last example — a simple text input, and a paragraph for output. The JavaScript is mostly the same too, although the loop itself is a bit different:

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 + ' ';
}

Here's the output:

  1. In this case, the input should be a number (num). The for loop is given a counter starting at 1 (as we are not interested in 0 in this case), an exit condition that says the loop will stop when the counter becomes bigger than the input num, and an iterator that adds 1 to the counter each time.
  2. Inside the loop, we find the square root of each number using Math.sqrt(i), then check whether the square root is an integer by testing whether it is the same as itself when it has been rounded down to the nearest integer (this is what Math.floor() does to the number it is passed).
  3. If the square root and the rounded down square root do not equal one another (!==), it means that the square root is not an integer, so we are not interested in it. In such a case, we use the continue statement to skip on to the next loop iteration without recording the number anywhere.
  4. If the square root IS an integer, we skip past the if block entirely so the continue statement is not executed; instead, we concatenate the current i value plus a space on to the end of the paragraph content.

: You can view the full source code on GitHub too (also see it running live).

while and do ... while

for is not the only type of loop available in JavaScript. There are actually many others and, while you don't need to understand all of these now, it is worth having a look at the structure of a couple of others so that you can recognize the same features at work in a slightly different way.

First, let's have a look at the while loop. This loop's syntax looks like so:

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

  final-expression
}

This works in a very similar way to the for loop, except that the initializer variable is set before the loop, and the final-expression is included inside the loop after the code to run — rather than these two items being included inside the parentheses. The exit-condition is included inside the parentheses, which are preceded by the while keyword rather than for.

The same three items are still present, and they are still defined in the same order as they are in the for loop — this makes sense, as you still have to have an initializer defined before you can check whether it has reached the exit-condition; the final-condition is then run after the code inside the loop has run (an iteration has been completed), which will only happen if the exit-condition has still not been reached.

Let's have a look again at our cats list example, but rewritten to use a while loop:

var i = 0;

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

  i++;
}

: This still works just the same as expected — have a look at it running live on GitHub (also view the full source code).

The do...while loop is very similar, but provides a variation on the while structure:

initializer
do {
  // code to run

  final-expression
} while (exit-condition)

In this case, the initializer again comes first, before the loop starts. The do keyword directly precedes the curly braces containing the code to run and the final-expression.

The differentiator here is that the exit-condition comes after everything else, wrapped in parentheses and preceded by a while keyword. In a do...while loop, the code inside the curly braces is always run once before the check is made to see if it should be executed again (in while and for, the check comes first, so the code might never be executed).

Let's rewrite our cat listing example again to use a do...while loop:

var i = 0;

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

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

: Again, this works just the same as expected — have a look at it running live on GitHub (also view the full source code).

Important: With while and do...while — as with all loops — you must make sure that the initializer is iterated so that it eventually reaches the exit condition. If not, the loop will go on forever, and either the browser will force it to stop, or it will crash. This is called an infinite loop.

Active learning: Launch countdown!

In this exercise, we want you to print out a simple launch countdown to the output box, from 10 down to Blast off. Specifically, we want you to:

  • Loop from 10 down to 0. We've provided you with an initializer — var i = 10;.
  • For each iteration, create a new paragraph and append it to the output <div>, which we've selected using var output = document.querySelector('.output');. In comments, we've provided you with three code lines that need to be used somewhere inside the loop:
    • var para = document.createElement('p'); — creates a new paragraph.
    • 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.
  • Different iteration numbers require different text to be put in the paragraph for that iteration (you'll need a conditional statement and multiple para.textContent = lines):
    • 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.
  • Remember to include an iterator! However, in this example we are counting down after each iteration, not up, so you don't want i++ — how do you iterate downwards?

If you make a mistake, you can always reset the example with the "Reset" button. If you get really stuck, press "Show solution" to see a solution.

Active learning: Filling in a guest list

In this exercise, we want you to take a list of names stored in an array, and put them into a guest list. But it's not quite that easy — we don't want to let Phil and Lola in because they are greedy and rude, and always eat all the food! We have two lists, one for guests to admit, and one for guests to refuse.

Specifically, we want you to:

  • Write a loop that will iterate from 0 to the length of the people array. You'll need to start with an initializer of  var i = 0;, but what exit condition do you need?
  • During each loop iteration, check if the current array item is equal to "Phil" or "Lola" using a conditional statement:
    • If it is, concatenate the array item to the end of the refused paragraph's textContent, followed by a comma and a space.
    • If it isn't, concatenate the array item to the end of the admitted paragraph's textContent, followed by a comma and a space.

We've already provided you with:

  • 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.

Extra bonus question — after completing the above tasks successfully, you will be left with two lists of names, separated by commas, but they will be untidy — there will be a comma at the end of each one. Can you work out how to write lines that slice the last comma off in each case, and add a full stop to the end? Have a look at the Useful string methods article for help.

If you make a mistake, you can always reset the example with the "Reset" button. If you get really stuck, press "Show solution" to see a solution.

Which loop type should you use?

For basic uses, for, while, and do...while loops are largely interchangeable. They can all be used to solve the same problems, and which one you use will largely depend on your personal preference — which one you find easiest to remember or most intuitive. Let's have a look at them again.

First for:

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

while:

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

  final-expression
}

and finally do...while:

initializer
do {
  // code to run

  final-expression
} while (exit-condition)

We would recommend for, at least to begin with, as it is probably the easiest for remembering everything — the initializer, exit-condition, and final-expression all have to go neatly into the parentheses, so it is easy to see where they are and check that you aren't missing them.

: There are other loop types/features too, which are useful in advanced/specialized situations and beyond the scope of this article. If you want to go further with your loop learning, read our advanced Loops and iteration guide.

Conclusion

This article has revealed to you the basic concepts behind, and different options available when, looping code in JavaScript. You should now be clear on why loops are a good mechanism for dealing with repetitive code, and be raring to use them in your own examples!

If there is anything you didn't understand, feel free to read through the article again, or contact us to ask for help.

関連情報

 

このモジュール内

 

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

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