ループと反復処理

ループは繰り返し何かを実行するための簡便な方法を提供します。本章では JavaScript で利用可能な反復処理を行う数々の文を紹介します。

ループは、ある方向に X 歩進み、次に別の方向に Y 歩進むように誰かに指示するゲームをコンピュータ化した版と考えることができます。例えば、「西に 5 歩進む」という概念はループとして、下記のように表現できます :

js
for (let step = 0; step < 5; step++) {
  // 値が 0 から 4 まで計 5 回実行される
  console.log("一歩西に歩く");
}

ループには様々な種類がありますが、本質的にはそれらはすべて同じことをしています : 何らかの行為をある回数繰り返すということです(ただしその回数がゼロということもありえます)。

様々なループ機構が存在することによって、色々な方法でループの開始点と終了点を決定することができます。あるループの方が別のタイプのループより簡単に目的を果たせる、という状況はたくさんあります。

JavaScript で提供されるループ文は以下のとおりです :

for

for 文によるループは指定された条件が false と評価されるまで繰り返されます。JavaScript の for ループは Java や C 言語の for ループと同じです。

for 文は以下のような形です :

for ([初期化式]; [条件式]; [加算式])
  文

for ループが実行されるとき、次の処理が行われます :

  1. もしあれば、初期化式が実行されます。この式は通常、1 個またはそれ以上のループカウンターを初期化しますが、この構文ではいかなるレベルの複雑な式を入れることが可能です。初期化式で変数を宣言することもできます。
  2. 条件式が評価されます。条件式の値が true の場合、ループ文が実行されます。条件式の値が false の場合、for ループは終了します。条件式がすべて省略されている場合、条件式は真であると仮定されます。
  3. が実行されます。複数の文を実行するには、それらの文をグループ化するためにブロック文 ({ ... }) を使用します。
  4. もしあれば、加算式が実行されます。
  5. ステップ 2 に制御が戻ります。

下の例では、スクロールリスト(複数選択できる <select> 要素)で選択したオプションの数をカウントする for 文が関数内にあります。for 文が変数 i を宣言しゼロに初期化します。i<select> 要素のオプション数未満であることがチェックされると、後続の if 文が実行され、オプションが選択されていれば、ループの各通過後に 1 個ずつ i が加算されます。

html
<form name="selectForm">
  <p>
    <label for="musicTypes"
      >Choose some music types, then click the button below:</label
    >
    <select id="musicTypes" name="musicTypes" multiple="multiple">
      <option selected="selected">R&B</option>
      <option>Jazz</option>
      <option>Blues</option>
      <option>New Age</option>
      <option>Classical</option>
      <option>Opera</option>
    </select>
  </p>
  <p><input id="btn" type="button" value="How many are selected?" /></p>
</form>

<script>
  function howMany(selectObject) {
    let numberSelected = 0;
    for (let i = 0; i < selectObject.options.length; i++) {
      if (selectObject.options[i].selected) {
        numberSelected++;
      }
    }
    return numberSelected;
  }

  let btn = document.getElementById("btn");
  btn.addEventListener("click", function () {
    alert(
      "Number of options selected: " + howMany(document.selectForm.musicTypes),
    );
  });
</script>

do...while

do...while 文は指定された条件が false になるまで繰り返します。

do...while 文は以下のような形です :

do
  文
while (条件式);

は条件式がチェックされる前に毎回一度実行されます。(複数の文を実行するには、ブロック文 ({ ... }) を使って一つの文にします)。

条件式true の場合、が再び実行されます。の実行終了時に毎回条件がチェックされます。条件が false の場合、実行は停止し、制御は do...while の次の文に渡ります。

次の例では、do ループは少なくとも一度繰り返されます。そして i が 5 未満でなくなる直前まで繰り返されます。

js
let i = 0;
do {
  i += 1;
  console.log(i);
} while (i < 5);

while

while 文は指定された条件が true に評価される限り文を実行します。while 文は以下のようになります :

while (条件式)
  文

条件式false となる場合、ループ内のは実行を停止し、ループに続く文に制御が渡されます。

ループ内のが実行されるに条件がテストされます。条件が true を返す場合、は実行され、条件が再びテストされます。条件が false を返す場合、実行は停止し、while の次の文に制御が渡されます。

複数の文を実行するには、それらの文をグループ化するブロック文 ({ ... }) を使用します。

例 1

次の例では、while ループは、n3 未満の場合繰り返されます。:

js
let n = 0;
let x = 0;
while (n < 3) {
  n++;
  x += n;
}

各反復で、n がインクリメントされ、その値が x に加算されます。そのため、xn は以下の値をとります:

  • 1 回目の反復後 : n = 1, x = 1
  • 2 回目の反復後 : n = 2, x = 3
  • 3 回目の反復後 : n = 3, x = 6

3 回目の通過完了後、条件式 n < 3 はもはや true ではなくなるため、ループが終了します。

例 2

無限ループは避けてください。ループ内の条件が最終的に false になることを確かめるようにしましょう。さもないと、ループは永遠に終了しません。次の while ループ文は、条件が決して false にならないので永遠に実行されます :

js
// 無限ループは悪!
while (true) {
  console.log("Hello, world!");
}

ラベルつき

label を使って、プログラム内の他の場所から参照できる識別子を組み込んだ文が作成できます。例えば、ループの識別にラベルを使い、そのプログラムでループを中断するか、実行を継続するかどうかを指定する場合に、ラベルを break 文や continue 文で使用することができます。

ラベル文の構文は以下のようになります :

ラベル :
   文

ラベル の値には、予約語でなければあらゆる JavaScript 識別子を使用できます。ラベルによって識別されるはどんな文でもかまいません。

この例では、ラベル markLoopwhile ループを指し示しています。

js
markLoop: while (theMark === true) {
  doSomething();
}

break

ループ、switch、ラベル文を使った関連付けを終了させるには break 文を使います。

  • ラベルなしで break を使用すると、最も内側の whiledo-whileforswitch をただちに終了し、次の文に制御を移します。この場合の break 文の構文は下記のようになります
  • ラベルつきで break を使用すると、指定したラベルつき文まで終了します。

break 文の構文は下記のようになります:

break;
break [ラベル];
  1. 最初の形では最も内側のループや switch を終了します
  2. 2 つ目の形では指定した外側のラベルつき文を終了します。

例 1

次の例では、値が theValue である要素のインデックスを探すまで配列の要素を反復します。:

js
for (let i = 0; i < a.length; i++) {
  if (a[i] === theValue) {
    break;
  }
}

例 2: ラベルまでブレーク

js
let x = 0;
let z = 0;
labelCancelLoops: while (true) {
  console.log("外側のループ: " + x);
  x += 1;
  z = 1;
  while (true) {
    console.log("内側のループ: " + z);
    z += 1;
    if (z === 10 && x === 10) {
      break labelCancelLoops;
    } else if (z === 10) {
      break;
    }
  }
}

continue

continue 文は whiledo-whileforlabel 文を再開する際に使用されます。

  • ラベルなしで continue を使用すると、その文を囲んでいる最も内側の whiledo-whilefor 文による現在の反復を終了し、次の反復のループの実行を継続します。break 文とは対照的に、continue は完全にループの実行を終了しません。while ループの場合、条件にジャンプします。for ループでは、加算式 にジャンプします。
  • ラベルとともに continue を使用すると、この動作がそのラベルによって識別されるループ文に対して適用されます。

continue文の構文は下記のようになります :

continue [ラベル];

例 1

次の例では、i の値が 3 のときに実行される continue 文が使用された while ループを示しています。この場合、n の値は 13712 となります。

js
let i = 0;
let n = 0;
while (i < 5) {
  i++;
  if (i === 3) {
    continue;
  }
  n += i;
  console.log(n);
}
//1,3,7,12

let i = 0;
let n = 0;
while (i < 5) {
  i++;
  if (i === 3) {
    // continue;
  }
  n += i;
  console.log(n);
}
// 1,3,6,10,15

例 2

次の例では、checkiandj とラベル付けされた文には checkj とラベル付けされた文が含まれています。continue 文に出会うと、プログラムは checkj の現在の反復を終了し次の反復を開始します。continue に出会うたびに、checkj はその条件が false を返すまで繰り返されます。false が返されると、checkiandj の残りの部分が完了し、その条件が false を返すまで checkiandj が繰り返されます。false が返されると、プログラムは checkiandj に続く文を継続します。

もし continuecheckiandj のラベルを持っていると、プログラムは checkiandj のラベル文の先頭から継続されます。

js
let i = 0;
let j = 10;
checkiandj: while (i < 4) {
  console.log(i);
  i += 1;
  checkj: while (j > 4) {
    console.log(j);
    j -= 1;
    if (j % 2 === 0) {
      continue checkj;
    }
    console.log(j + " is odd.");
  }
  console.log("i = " + i);
  console.log("j = " + j);
}

for...in

for...in 文はオブジェクトにあるすべての列挙可能なプロパティに対し指定された変数を通して反復処理を行います。それぞれの異なるプロパティに、JavaScript は指定された文を実行します。for...in 文は下記のようになります :

for (変数 in オブジェクト)
  文

次の関数は引数としてオブジェクトと、そのオブジェクトの名前を表す文字列を取ります。それからすべてのオブジェクトのプロパティに対して反復し、プロパティ名とその値を表示する文字列を返します。

js
function dump_props(obj, obj_name) {
  let result = "";
  for (let i in obj) {
    result += obj_name + "." + i + " = " + obj[i] + "<br>";
  }
  result += "<hr>";
  return result;
}

make プロパティと model プロパティを持つ car オブジェクトに対し、result は下記のようになります :

js
car.make = Ford;
car.model = Mustang;

配列

Array の要素に対して反復処理を行う方法として for...in 文を使用したくなるかもしれませんが、これは数値のインデックスに加えてユーザー定義プロパティの名前も返します。

そのため、配列に対しての反復処理には、数値のインデックスを使い従来の for ループを使用するほうが良いです。なぜなら、(カスタムプロパティやカスタムメソッドを追加するといった) Array オブジェクトの変更を行った場合、for...in 文は配列要素に加えてユーザー定義プロパティに対しても反復処理するからです。

for...of

for...of 文は、反復可能オブジェクトArrayMapSetarguments オブジェクトなどを含む)を反復処理するループを生成し、それぞれのプロパティの値に対して実行したい文をともなって作られた反復処理フックを呼び出します。

for (変数 of オブジェクト)
  文

次の例では、for...of ループと for...in ループとの違いを示しています。for...in はプロパティ名に対し反復処理される一方、for...of はプロパティの値に対し反復処理します :

js
const arr = [3, 5, 7];
arr.foo = "hello";

for (let i in arr) {
  console.log(i); // "0", "1", "2", "foo" が出力される
}

for (let i of arr) {
  console.log(i); // 3, 5, 7 が出力される
}