JavaScript for impatient programmers (ES2022 edition)
この書籍をサポートしてください: 購入する または 寄付する
(広告です。ブロックしないでください。)

23章 制御フロー文



この章では、以下の制御フロー文について説明します。

23.1章 ループの制御: breakcontinue

2 つの演算子 breakcontinue は、ループや他の文の内部にいる間にそれらを制御するために使用できます。

23.1.1章 break

break には、オペランドを持つバージョンとオペランドを持たないバージョンの 2 つがあります。後者のバージョンは、whiledo-whileforfor-offor-await-offor-inswitch 文の内部で動作します。現在の文をすぐに終了します。

for (const x of ['a', 'b', 'c']) {
  console.log(x);
  if (x === 'b') break;
  console.log('---')
}

// Output:
// 'a'
// '---'
// 'b'

23.1.2章 break とラベル: ラベル付き文からの脱出

オペランドを持つ break は、どこでも動作します。そのオペランドは *ラベル* です。ラベルは、ブロックを含む任意の文の前に置くことができます。 break my_label は、ラベルが my_label である文を終了します。

my_label: { // label
  if (condition) break my_label; // labeled break
  // ···
}

次の例では、検索は次のいずれかの結果になります。

function findSuffix(stringArray, suffix) {
  let result;
  search_block: {
    for (const str of stringArray) {
      if (str.endsWith(suffix)) {
        // Success:
        result = str;
        break search_block; // (A)
      }
    } // for
    // Failure:
    result = '(Untitled)'; // (B)
  } // search_block

  return { suffix, result };
    // Same as: {suffix: suffix, result: result}
}
assert.deepEqual(
  findSuffix(['notes.txt', 'index.html'], '.html'),
  { suffix: '.html', result: 'index.html' }
);
assert.deepEqual(
  findSuffix(['notes.txt', 'index.html'], '.mjs'),
  { suffix: '.mjs', result: '(Untitled)' }
);

23.1.3章 continue

continue は、whiledo-whileforfor-offor-await-offor-in の内部でのみ動作します。現在のループの反復処理をすぐに終了し、次の反復処理を続行します。例:

const lines = [
  'Normal line',
  '# Comment',
  'Another normal line',
];
for (const line of lines) {
  if (line.startsWith('#')) continue;
  console.log(line);
}
// Output:
// 'Normal line'
// 'Another normal line'

23.2章 制御フロー文の条件

ifwhiledo-while には、原則としてブール値である条件があります。ただし、条件は *truthy* (ブール値に強制変換された場合に true) であるだけで受け入れられます。つまり、次の 2 つの制御フロー文は同等です。

if (value) {}
if (Boolean(value) === true) {}

これは、すべての *falsy* 値のリストです。

他のすべての値は truthy です。詳細については、§15.2 “Falsy な値と truthy な値” を参照してください。

23.3章 if 文 [ES1]

これらは 2 つの単純な if 文です。「then」分岐のみを持つものと、「then」分岐と「else」分岐の両方を持つものです。

if (cond) {
  // then branch
}

if (cond) {
  // then branch
} else {
  // else branch
}

ブロックの代わりに、else の後に別の if 文を続けることもできます。

if (cond1) {
  // ···
} else if (cond2) {
  // ···
}

if (cond1) {
  // ···
} else if (cond2) {
  // ···
} else {
  // ···
}

このチェーンをさらに else if で続けることができます。

23.3.1章 if 文の構文

if 文の一般的な構文は次のとおりです。

if (cond) «then_statement»
else «else_statement»

これまでのところ、then_statement は常にブロックでしたが、任意の文を使用できます。その文はセミコロンで終了する必要があります。

if (true) console.log('Yes'); else console.log('No');

つまり、else if はそれ自体が構成要素ではなく、単に else_statement が別の if 文である if 文です。

23.4章 switch 文 [ES3]

switch 文は次のようになります。

switch («switch_expression») {
  «switch_body»
}

switch の本体は、ゼロ個以上の case 句で構成されます。

case «case_expression»:
  «statements»

また、オプションで default 句を含めることができます。

default:
  «statements»

switch は次のように実行されます。

23.4.1章 switch 文の最初の例

例を見てみましょう。次の関数は、1〜7 の数値を曜日の名前に変換します。

function dayOfTheWeek(num) {
  switch (num) {
    case 1:
      return 'Monday';
    case 2:
      return 'Tuesday';
    case 3:
      return 'Wednesday';
    case 4:
      return 'Thursday';
    case 5:
      return 'Friday';
    case 6:
      return 'Saturday';
    case 7:
      return 'Sunday';
  }
}
assert.equal(dayOfTheWeek(5), 'Friday');

23.4.2章 return または break を忘れないでください!

case 句の最後で、return または break しない限り、実行は次の case 句に続きます。例:

function englishToFrench(english) {
  let french;
  switch (english) {
    case 'hello':
      french = 'bonjour';
    case 'goodbye':
      french = 'au revoir';
  }
  return french;
}
// The result should be 'bonjour'!
assert.equal(englishToFrench('hello'), 'au revoir');

つまり、dayOfTheWeek() の実装は、return を使用したためだけに機能しました。 break を使用することで englishToFrench() を修正できます。

function englishToFrench(english) {
  let french;
  switch (english) {
    case 'hello':
      french = 'bonjour';
      break;
    case 'goodbye':
      french = 'au revoir';
      break;
  }
  return french;
}
assert.equal(englishToFrench('hello'), 'bonjour'); // ok

23.4.3章 空の case 句

case 句の文を省略することができます。これにより、事実上、case 句ごとに複数の case 式を使用できます。

function isWeekDay(name) {
  switch (name) {
    case 'Monday':
    case 'Tuesday':
    case 'Wednesday':
    case 'Thursday':
    case 'Friday':
      return true;
    case 'Saturday':
    case 'Sunday':
      return false;
  }
}
assert.equal(isWeekDay('Wednesday'), true);
assert.equal(isWeekDay('Sunday'), false);

23.4.4章 default 句による不正な値のチェック

switch 式に他の合致がない場合、default 句にジャンプします。これはエラーチェックに役立ちます。

function isWeekDay(name) {
  switch (name) {
    case 'Monday':
    case 'Tuesday':
    case 'Wednesday':
    case 'Thursday':
    case 'Friday':
      return true;
    case 'Saturday':
    case 'Sunday':
      return false;
    default:
      throw new Error('Illegal value: '+name);
  }
}
assert.throws(
  () => isWeekDay('January'),
  {message: 'Illegal value: January'});

  練習問題: switch

23.5章 while ループ [ES1]

while ループの構文は次のとおりです。

while («condition») {
  «statements»
}

各ループの反復処理の前に、whilecondition を評価します。

23.5.1章 while ループの例

次のコードは while ループを使用しています。各ループの反復処理で、.shift() を使用して arr の最初の要素を削除し、それをログに記録します。

const arr = ['a', 'b', 'c'];
while (arr.length > 0) {
  const elem = arr.shift(); // remove first element
  console.log(elem);
}
// Output:
// 'a'
// 'b'
// 'c'

条件が常に true と評価される場合、while は無限ループになります。

while (true) {
  if (Math.random() === 0) break;
}

23.6章 do-while ループ [ES3]

do-while ループは while とよく似ていますが、条件を各ループの反復処理の *後* にチェックします (前ではありません)。

let input;
do {
  input = prompt('Enter text:');
  console.log(input);
} while (input !== ':q');

do-while は、少なくとも 1 回は実行される while ループと見なすこともできます。

prompt() は、Web ブラウザで使用できるグローバル関数です。ユーザーにテキストの入力を促し、それを返します。

23.7章 for ループ [ES1]

for ループの構文は次のとおりです。

for («initialization»; «condition»; «post_iteration») {
  «statements»
}

最初の行はループの *ヘッド* で、*本体* (ループの残りの部分) が何回実行されるかを制御します。3 つの部分があり、それぞれがオプションです。

したがって、for ループは、次の while ループとほぼ同等です。

«initialization»
while («condition») {
  «statements»
  «post_iteration»
}

23.7.1章 for ループの例

例として、for ループを使用して 0 から 2 までカウントする方法は次のとおりです。

for (let i=0; i<3; i++) {
  console.log(i);
}

// Output:
// 0
// 1
// 2

for ループを使用して配列の内容をログに記録する方法は次のとおりです。

const arr = ['a', 'b', 'c'];
for (let i=0; i<arr.length; i++) {
  console.log(arr[i]);
}

// Output:
// 'a'
// 'b'
// 'c'

ヘッドの 3 つの部分をすべて省略すると、無限ループになります。

for (;;) {
  if (Math.random() === 0) break;
}

23.8章 for-of ループ [ES6]

for-of ループは、任意の *イテラブル* ( 反復プロトコル をサポートするデータコンテナ) を反復処理します。反復処理された各値は、ヘッドで指定された変数に格納されます。

for («iteration_variable» of «iterable») {
  «statements»
}

反復変数は通常、変数宣言によって作成されます。

const iterable = ['hello', 'world'];
for (const elem of iterable) {
  console.log(elem);
}
// Output:
// 'hello'
// 'world'

しかし、既に存在する (変更可能な) 変数を使用することもできます。

const iterable = ['hello', 'world'];
let elem;
for (elem of iterable) {
  console.log(elem);
}

23.8.1章 const: for-of vs. for

for-of ループでは const を使用できることに注意してください。反復変数は、反復処理ごとに異なる可能性があります (反復処理中に変更できないだけです)。新しいスコープで毎回新しい const 宣言が実行されると考えてください。

対照的に、for ループでは、値が変化する場合、変数を let または var を使用して宣言する必要があります。

23.8.2章 イテラブルの反復処理

前述のように、for-of は配列だけでなく、任意のイテラブルオブジェクト (たとえば、Set) で動作します。

const set = new Set(['hello', 'world']);
for (const elem of set) {
  console.log(elem);
}

23.8.3章 配列の [index, element] ペアの反復処理

最後に、for-of を使用して配列の [index, element] エントリを反復処理することもできます。

const arr = ['a', 'b', 'c'];
for (const [index, elem] of arr.entries()) {
  console.log(`${index} -> ${elem}`);
}
// Output:
// '0 -> a'
// '1 -> b'
// '2 -> c'

[index, element] を使用すると、 *分割代入* を使用して配列要素にアクセスしています。

  **練習問題: for-of**

exercises/control-flow/array_to_string_test.mjs

23.9章 for-await-of ループ [ES2018]

for-await-offor-of に似ていますが、同期イテラブルではなく非同期イテラブルで動作します。また、非同期関数と非同期ジェネレーター内でのみ使用できます。

for await (const item of asyncIterable) {
  // ···
}

for-await-of については、非同期反復処理の章 で詳しく説明しています。

23.10章 for-in ループ (避ける) [ES1]

for-inループは、オブジェクトのすべての(自身および継承された)列挙可能なプロパティキーを走査します。配列をループする場合、これはめったに良い選択ではありません。

以下のコードはこれらの点を示しています。

const arr = ['a', 'b', 'c'];
arr.propKey = 'property value';

for (const key in arr) {
  console.log(key);
}

// Output:
// '0'
// '1'
// '2'
// 'propKey'

23.11 ループに関する推奨事項

  クイズ

クイズアプリをご覧ください。