ウェブエンジニア問題集

制御構文 — if・switch・for・whileの実務パターン

JavaScriptの制御構文は、コードの実行順序を変えるための仕組みです。条件によって処理を分岐したり、繰り返したり、途中で抜け出したりできます。

この章では実務でよく使うパターンを中心に整理します。

学習者学習者

ifswitch はどう使い分けるの?breakreturn を書き忘れてバグを出したことがある…。


条件分岐:if / else

最も基本的な制御構文です。条件が truthy か falsy かによって、実行するブロックを切り替えます。

const score = 75;
 
if (score >= 90) {
  console.log('優');
} else if (score >= 70) {
  console.log('良'); // こちらが実行される
} else {
  console.log('可');
}

早期 return パターン

ネストが深くなりがちな条件分岐は、**早期 return(Guard Clause)**で扁平にできます。

// ネストが深くなるパターン
function processOrder(order) {
  if (order) {
    if (order.isPaid) {
      if (order.items.length > 0) {
        // 本来の処理
        ship(order);
      }
    }
  }
}
 
// 早期 return で扁平にしたパターン
function processOrder(order) {
  if (!order) return;
  if (!order.isPaid) return;
  if (order.items.length === 0) return;
 
  // 本来の処理
  ship(order);
}

条件分岐:switch

複数の値と比較するときは switch が読みやすくなります。

const day = 'Monday';
 
switch (day) {
  case 'Saturday':
  case 'Sunday':
    console.log('週末');
    break;
  case 'Monday':
    console.log('月曜日'); // こちらが実行される
    break;
  default:
    console.log('平日');
}

フォールスルーに注意

break を書き忘れると、次の case の処理も続けて実行されます(フォールスルー)。

switch (status) {
  case 'pending':
    console.log('pending');
    // break がないと次の case も実行される!
  case 'active':
    console.log('active'); // status が 'pending' のときも実行される
    break;
}

意図的なフォールスルー(複数の値に同じ処理)以外は、必ず break を書きます。


ループ:for / for...of / for...in

for 文

インデックスが必要な場合や、回数を指定して繰り返すときに使います。

for (let i = 0; i < 5; i++) {
  console.log(i); // 0, 1, 2, 3, 4
}

for...of

配列や文字列などのイテラブルを順に処理するときに使います。インデックスが不要なケースはこちらが読みやすいです。

const fruits = ['apple', 'banana', 'cherry'];
 
for (const fruit of fruits) {
  console.log(fruit);
}
 
// インデックスも必要な場合は entries()
for (const [index, fruit] of fruits.entries()) {
  console.log(index, fruit); // 0 'apple', 1 'banana', ...
}

for...in

オブジェクトの列挙可能なプロパティキーを順に処理します。

const user = { name: 'Alice', age: 30 };
 
for (const key in user) {
  console.log(key, user[key]); // 'name' 'Alice', 'age' 30
}

while / do...while

条件が満たされる間、繰り返し実行します。

let count = 0;
while (count < 3) {
  console.log(count); // 0, 1, 2
  count++;
}
 
// do...while は最低1回は実行される
let n = 10;
do {
  console.log(n); // 10(条件を満たさなくても1回実行)
  n++;
} while (n < 5);

break と continue

break

ループや switch を途中で抜け出します。

const numbers = [1, 3, 5, 2, 8, 4];
 
for (const n of numbers) {
  if (n % 2 === 0) {
    console.log(`最初の偶数: ${n}`); // '最初の偶数: 2'
    break; // ループを終了
  }
}

continue

現在のイテレーションをスキップして、次のイテレーションに進みます。

for (let i = 0; i < 5; i++) {
  if (i % 2 === 0) continue; // 偶数はスキップ
  console.log(i); // 1, 3
}

return と throw — 制御フロー文

returnthrow は、iffor と同様に**制御フロー文(Control Flow Statement)**です。どちらも、その後のコードを実行せずに処理の流れを変えます。

return — 関数を終了して値を返す

return は関数の実行をそこで終了し、呼び出し元に値を返します。

function divide(a, b) {
  if (b === 0) {
    return null; // ここで関数終了。以降は実行されない
  }
  return a / b;
}
 
console.log(divide(10, 2)); // 5
console.log(divide(10, 0)); // null

return の後ろに書くもの(nulla / b)は**式(Expression)**です。return 自体は文なので、変数に代入したりできません。

// return は文なので、式としては使えない
const x = return 5; // SyntaxError

return だけ書いた場合、または return を省略した関数は undefined を返します。

function doSomething() {
  console.log('実行');
  return; // undefined を返す
}
 
function doNothing() {
  // return がない場合も undefined を返す
}

throw — 例外を投げて処理を中断する

throw は例外を発生させ、呼び出し元に向かってスタックを遡ります。try...catch でキャッチされるまで伝播し続けます。

function getUser(id) {
  if (!id) {
    throw new Error('id は必須です'); // ここで関数終了。例外を投げる
  }
  return fetchUser(id);
}
 
try {
  getUser(null);
} catch (error) {
  console.log(error.message); // 'id は必須です'
}

throw の後ろに書くもの(new Error(...))もです。技術的には任意の値を投げられますが、Error オブジェクト(またはそのサブクラス)を使うのが慣習です。

throw new Error('メッセージ');          // 推奨
throw new TypeError('型が違います');    // 組み込みエラー型
throw new RangeError('範囲外です');     // 組み込みエラー型
throw 'エラー文字列';                   // 技術的には可能だが非推奨
throw 42;                               // 同上

return と throw の使い分け

状況使うもの
正常な終了(値を返す)return
値を返さずに終了returnundefined が返る)
想定内の「早期終了」(ガード節)return
呼び出し元に問題を知らせたいthrow
引数が不正・前提条件を満たさないthrow
// 正常な早期終了 → return
function formatName(name) {
  if (!name) return '名前未設定'; // 値として返せるのでreturnでよい
  return name.trim();
}
 
// 呼び出し元に問題を知らせる → throw
function parsePositiveInt(str) {
  const num = parseInt(str, 10);
  if (isNaN(num)) {
    throw new TypeError(`'${str}' は数値に変換できません`);
  }
  if (num <= 0) {
    throw new RangeError(`正の整数が必要です: ${num}`);
  }
  return num;
}

まとめ

構文役割
if / else条件によって分岐
switch単一値の多分岐(break 必須)
for...ofイテラブルの反復
for...inオブジェクトのキー列挙(配列には使わない)
while条件が真の間繰り返す
breakループ・switch を抜ける
continue今のイテレーションをスキップ
return関数を終了して値を返す(制御フロー文)
throw例外を投げて処理を中断(制御フロー文)

throw を使った例外のキャッチ(try...catch)やカスタムエラークラスの詳細は、14章「エラーハンドリング」で扱います。

ステップアップしている男性のイラスト

次の章では、関数の定義方法と this の挙動を詳しく見ていきます。