JavaScript switch文をすぐに使える!書き方と具体例

JavaScript switch文をすぐに使える!書き方から応用、注意点まで徹底解説(約5000語)

はじめに:なぜ条件分岐が必要なのか?switch文の立ち位置

プログラミングにおいて、処理の流れを状況に応じて変える「条件分岐」は最も基本的な概念の一つです。ユーザーの入力値によって表示を変えたり、データの状態に応じて異なる計算を行ったり、エラーの種類によって異なるエラーメッセージを出したりと、現実世界の複雑な問題に対応するためには、条件分岐が不可欠です。

JavaScriptにおける条件分岐の最も一般的な方法は、if文とelse if文を組み合わせたものです。

“`javascript
let score = 85;
let grade;

if (score >= 90) {
grade = ‘秀’;
} else if (score >= 80) {
grade = ‘優’;
} else if (score >= 70) {
grade = ‘良’;
} else if (score >= 60) {
grade = ‘可’;
} else {
grade = ‘不可’;
}

console.log(点数: ${score}, 評価: ${grade}); // 点数: 85, 評価: 優
“`

このif...else ifの連鎖は、非常に柔軟性が高く、様々な条件式(比較演算子、論理演算子などを含む)を使うことができます。しかし、もしあなたが「ある一つの値」を、複数の「特定の値」と比較して処理を分岐させたい場合、このif...else ifの連鎖は少々冗長に感じられることがあります。

例えば、曜日に応じて異なるメッセージを表示したい場合を考えてみましょう。

“`javascript
let day = ‘月曜日’;
let message;

if (day === ‘月曜日’) {
message = ‘一週間の始まり!頑張りましょう。’;
} else if (day === ‘火曜日’) {
message = ‘火曜日は燃える日!’;
} else if (day === ‘水曜日’) {
message = ‘週の真ん中、折り返し地点!’;
} else if (day === ‘木曜日’) {
message = ‘木曜日、もうひと踏ん張り!’;
} else if (day === ‘金曜日’) {
message = ‘やったー!週末まであと少し!’;
} else if (day === ‘土曜日’ || day === ‘日曜日’) {
message = ‘楽しい週末を!’;
} else {
message = ‘不正な曜日です。’;
}

console.log(message); // 一週間の始まり!頑張りましょう。
“`

このコードでは、dayという一つの変数に対して、繰り返しday === ...という比較を行っています。このように、一つの値が複数の「特定の値」のいずれかに一致するかどうかで処理を分けたい場合に、JavaScriptのswitch文は非常に有効な選択肢となります。

switch文は、この種の条件分岐をより明確かつ簡潔に記述するために設計されています。これは、コードの可読性を高め、記述ミスを減らすのに役立ちます。

この記事では、JavaScriptのswitch文を「すぐに使える」レベルから始め、その基本的な書き方各要素の詳細具体的な使用例知っておくべき注意点や応用テクニック、そしてif...else if文との比較と使い分けに至るまで、約5000語をかけて徹底的に解説します。これを読めば、あなたはswitch文を自信を持って使いこなし、より効率的で読みやすいJavaScriptコードを書けるようになるでしょう。

さあ、switch文の世界へ飛び込みましょう!

第1章: switch文の基本を理解する

まずは、switch文の基本的な構文と、それを構成する各要素の役割から見ていきましょう。

1.1 switch文の基本構文

switch文の基本的な形は以下のようになります。

javascript
switch (式) {
case 値1:
// 式が値1と一致した場合に実行されるコード
break; // ここで処理を終了
case 値2:
// 式が値2と一致した場合に実行されるコード
break; // ここで処理を終了
// ... 他のcaseラベル ...
default:
// どのcaseにも一致しなかった場合に実行されるコード
// breakは省略可能(switch文の最後のため)
}

この構文には、以下の重要なキーワードが登場します。

  • switch
  • case
  • break
  • default

それぞれの役割を見ていきましょう。

1.2 各キーワードの詳細な説明

switch (式)

switch文は、まずswitchキーワードに続く括弧 () 内の「式」を評価します。この「式」の結果得られる値が、その後のcaseラベルの値と比較される対象となります。

式には、変数、リテラル、計算式、関数の呼び出しなど、任意のJavaScript式を書くことができます。評価の結果は、数値、文字列、真偽値など、プリミティブな値であることが一般的です(オブジェクトや配列も可能ですが、比較は参照の一致によって行われるため注意が必要です。詳細は後述します)。

例:

“`javascript
switch (dataType) { // dataType 変数の値が評価される
// …
}

switch (input.charCodeAt(0)) { // input文字列の最初の文字の文字コードが評価される
// …
}

switch (status === ‘active’) { // statusが’active’かどうかという真偽値が評価される
// …
}
“`

case 値:

caseキーワードは、switch式の結果と比較するための特定の値を指定します。caseに続くは、リテラル(数値、文字列、真偽値、null, undefined)や定数など、静的に決定できる値である必要があります。変数や計算式を直接caseの値として使うことはできません。

switch式の結果が、いずれかのcaseラベルの厳密等価演算子 (===) を使って比較されます。つまり、値だけでなく、型も一致している必要があります

一致するcaseが見つかると、そのcaseラベルの直後から、次にbreak;またはswitch文の終了が現れるまでの間のコードが実行されます。

複数のcaseラベルを連続して書くことで、同じ処理を複数の値に適用することができます。

例:

“`javascript
let fruit = ‘apple’;

switch (fruit) {
case ‘apple’: // fruit === ‘apple’ ?
console.log(‘これはリンゴです。’);
break;
case ‘banana’: // fruit === ‘banana’ ?
console.log(‘これはバナナです。’);
break;
case ‘orange’: // fruit === ‘orange’ ?
console.log(‘これはオレンジです。’);
break;
default:
console.log(‘他の果物です。’);
}
“`

break;

break;キーワードは、現在のcaseブロックでの処理を終了し、switch文全体から抜け出すために使用されます。

一致するcaseが見つかり、その中のコードが実行された後、break;に到達すると、switch文の閉じ波括弧 } の直後にあるコードに処理が移ります。

ほとんどの場合、各caseブロックの最後にはbreak;を記述する必要があります。 これを忘れると、予期しない重要な動作が発生します。詳細は後述します。

default:

default:キーワードは、switch式の結果がどのcaseラベルの値とも一致しなかった場合に実行されるブロックを指定します。これはif...else if文における最後のelseブロックに相当します。

defaultブロックは省略可能ですが、多くの場合、予期しない値が与えられた場合の処理(例: エラーメッセージの表示、デフォルト値の設定など)のために含めることが推奨されます。

defaultブロックは、switch文内のどこにでも記述できますが、慣習として最後に記述されることが多いです。もしdefaultブロックを最後に記述する場合、その後に続くbreak;は省略可能です(switch文の終了によって自動的に抜けるため)。しかし、途中に記述する場合は、意図的にフォールスルーさせたい場合を除き、break;が必要です。

例:

“`javascript
let color = ‘purple’;

switch (color) {
case ‘red’:
console.log(‘色は赤です。’);
break;
case ‘blue’:
console.log(‘色は青です。’);
break;
default: // ‘purple’はどのcaseとも一致しないため、ここが実行される
console.log(‘色は赤でも青でもありません。’);
// 最後のdefaultなのでbreakは省略可能
}
“`

1.3 breakがない場合の「フォールスルー」とは?

switch文において、一致するcaseが見つかった場合、そのcaseブロック内のコードが実行されます。もしそのブロックの最後にbreak;記述されていないと、処理はそのまま次のcaseラベル、あるいはdefaultラベルへと継続して実行されます。この動作を「フォールスルー (fall-through)」と呼びます。

意図しないフォールスルーは、switch文で最も起こりやすいバグの原因の一つです。caseの処理が独立しているつもりでbreakを書き忘れると、後続のcaseの処理まで意図せず実行されてしまい、プログラムの挙動がおかしくなります。

例:意図しないフォールスルー

“`javascript
let num = 2;

switch (num) {
case 1:
console.log(‘ケース 1’);
// break がない!
case 2:
console.log(‘ケース 2’); // num === 2 なのでここから実行開始
// break がない!
case 3:
console.log(‘ケース 3’); // フォールスルーによりここも実行される
break; // ここでswitch文を抜ける
default:
console.log(‘デフォルト’);
}
// 実行結果:
// ケース 2
// ケース 3
“`

この例では、num2なのでcase 2:から処理が開始されます。しかし、case 2:の最後にbreakがないため、処理はそのまま次のcase 3:へフォールスルーし、ケース 3も出力されます。

1.4 フォールスルーの意図的な利用

フォールスルーは、意図的に利用されることもあります。最も一般的な使い方は、複数のcaseラベルに対して同じ処理を実行したい場合です。この場合、最初のcaseラベルだけ処理を書き、他のcaseラベルには何も書かずにフォールスルーさせ、最後のcaseに処理とbreakを書きます。

例:複数のケースで同じ処理

先ほどの曜日の例で、土曜日と日曜日に同じメッセージを表示する場合を考えてみましょう。

“`javascript
let day = ‘土曜日’;
let message;

switch (day) {
case ‘月曜日’:
message = ‘一週間の始まり!頑張りましょう。’;
break;
case ‘火曜日’:
message = ‘火曜日は燃える日!’;
break;
case ‘水曜日’:
message = ‘週の真ん中、折り返し地点!’;
break;
case ‘木曜日’:
message = ‘木曜日、もうひと踏ん張り!’;
break;
case ‘金曜日’:
message = ‘やったー!週末まであと少し!’;
break;
case ‘土曜日’: // ここから一致した場合
case ‘日曜日’: // フォールスルーしてここに来る
message = ‘楽しい週末を!’; // ここが実行される
break; // ここで抜ける
default:
message = ‘不正な曜日です。’;
// 最後のdefaultなのでbreakは省略可能
}

console.log(message); // 楽しい週末を!
“`

このように、case '土曜日':の後に何も書かずにcase '日曜日':を続けることで、dayが’土曜日’でも’日曜日’でも、その下のmessage = '楽しい週末を!';が実行されるようになります。これはフォールスルーの便利な使い方です。

ただし、意図的なフォールスルーであることをコードを読む人に明確に伝えるために、コメントを記述することが推奨される場合があります。

1.5 defaultの位置と省略

default:ブロックは、switch文のどこにでも配置できます。しかし、多くの場合、コードの可読性を高めるために最後に配置するのが一般的です。

default:ブロックは省略可能です。switch式の結果がどのcaseラベルとも一致せず、defaultブロックも存在しない場合、switch文内のどのコードも実行されず、そのままswitch文の次の行に処理が移ります。

例:途中にdefaultがある場合

“`javascript
let value = 5;

switch (value) {
case 1:
console.log(‘ケース 1’);
break;
default: // どのケースにも一致しない場合に実行
console.log(‘デフォルト処理’);
break; // defaultが最後ではないのでbreakが必要
case 2:
console.log(‘ケース 2’);
break;
}
// 実行結果:
// デフォルト処理
“`

この例では、value5なのでどのcaseにも一致せず、defaultが実行されます。defaultの後にbreakがあるため、case 2は実行されません。もしdefaultbreakがなければ、case 2にフォールスルーします。

可読性の観点から、default特別な理由がない限り最後に記述するのが良いでしょう。

第2章: switch文を使いこなす – 具体的なコード例

基本構文を理解したところで、さまざまな状況でswitch文をどのように活用できるか、具体的なコード例を通して見ていきましょう。

2.1 簡単な数値による分岐

最も基本的な使い方は、数値と定数を比較する場合です。

``javascript
function processErrorCode(errorCode) {
let message;
switch (errorCode) {
case 0:
message = '処理が正常に完了しました。';
break;
case 400:
message = 'リクエストが不正です。';
break;
case 404:
message = '指定されたリソースが見つかりません。';
break;
case 500:
message = 'サーバー内部エラーが発生しました。';
break;
default:
message = '不明なエラーコードです。';
}
console.log(
エラーコード ${errorCode}: ${message}`);
}

processErrorCode(0); // エラーコード 0: 処理が正常に完了しました。
processErrorCode(404); // エラーコード 404: 指定されたリソースが見つかりません。
processErrorCode(999); // エラーコード 999: 不明なエラーコードです。
“`

このように、特定のエラーコードに対して決まった処理を行う場合にswitch文は非常に分かりやすいです。各caseは、期待されるエラーコードと直接的に対応しています。

2.2 文字列による分岐

文字列を比較するケースも非常に多いです。ユーザーからのコマンド入力や、設定値などに応じて処理を分けたい場合などに適しています。

``javascript
function handleCommand(command) {
let action;
switch (command.toLowerCase()) { // 小文字に変換して比較
case 'open':
action = 'ファイルを開きます。';
break;
case 'save':
action = 'ファイルを保存します。';
break;
case 'close':
action = 'ファイルを閉じます。';
break;
case 'print':
action = 'ファイルを印刷します。';
break;
default:
action =
不明なコマンド ‘${command}’ です。`;
}
console.log(action);
}

handleCommand(‘Open’); // ファイルを開きます。 (toLowerCase()で’open’になる)
handleCommand(‘Save’); // ファイルを保存します。
handleCommand(‘delete’); // 不明なコマンド ‘delete’ です。
“`

ここでは、入力されたコマンド文字列を小文字に変換してからswitchで比較しています。これにより、大文字・小文字を区別せずに入力を受け付けることができます。

2.3 複数のケースをまとめる(意図的なフォールスルーの活用)

前述したように、複数のcaseラベルに対して同じ処理を実行したい場合にフォールスルーを利用します。

例:週末判定

``javascript
function checkWeekend(dayOfWeek) {
let type;
switch (dayOfWeek) {
case '月':
case '火':
case '水':
case '木':
case '金':
type = '平日';
break;
case '土':
case '日':
type = '週末';
break;
default:
type = '不明な入力';
}
console.log(
${dayOfWeek}曜日は${type}です。`);
}

checkWeekend(‘月’); // 月曜日は平日です。
checkWeekend(‘土’); // 土曜日は週末です。
checkWeekend(‘水’); // 水曜日は平日です。
checkWeekend(‘祝’); // 祝日は不明な入力です。
“`

この例では、月曜日から金曜日までの各caseは、その下にbreakがないため、type = '平日';の行までフォールスルーします。同様に、土曜日と日曜日はtype = '週末';の行までフォールスルーします。これにより、冗長なコードを書かずに複数の条件をまとめられます。

2.4 truefalseをスイッチ式にする高度なテクニック(範囲判定など)

switch文の式には、必ずしも変数やリテラルだけでなく、任意の式を書くことができます。そして、その評価結果はどのような型でも構いません(比較が===で行われる点に注意が必要ですが)。

この性質を利用して、switch式を常にtrueにし、各caseラベルに条件式を記述するというテクニックがあります。これは、if...else ifで書くと冗長になる特定の範囲による条件分岐などで非常に有効です。

例:点数による評価(再掲)

最初の例で示した点数による評価を、switch (true)を使って書き直してみましょう。

``javascript
function evaluateScore(score) {
let grade;
switch (true) { // スイッチ式は常にtrue
case score >= 90: // case true: と比較される
grade = '秀';
break;
case score >= 80: // case true: と比較される (scoreが90未満の場合)
grade = '優';
break;
case score >= 70: // case true: と比較される (scoreが80未満の場合)
grade = '良';
break;
case score >= 60: // case true: と比較される (scoreが70未満の場合)
grade = '可';
break;
default: // どのcaseの条件式もfalseだった場合
grade = '不可';
}
console.log(
点数: ${score}, 評価: ${grade}`);
}

evaluateScore(95); // 点数: 95, 評価: 秀
evaluateScore(82); // 点数: 82, 評価: 優
evaluateScore(68); // 点数: 68, 評価: 可
evaluateScore(45); // 点数: 45, 評価: 不可
“`

このコードの仕組みを解説します。

  1. switch (true): ここでスイッチ式が評価され、結果は常に論理値のtrueになります。
  2. case score >= 90:: 最初のcaseラベルです。score >= 90という条件式が評価されます。もしscoreが95なら、この式はtrueになります。
  3. JavaScriptは、switch式の結果 (true) と、caseラベルの値 (score >= 90の評価結果、つまりtrue) を厳密に比較します (true === true)。一致しました!
  4. したがって、grade = '秀';が実行され、breakswitch文を抜けます。

もしscoreが82だった場合:

  1. switch (true)の結果はtrueです。
  2. case score >= 90: の条件式は82 >= 90となり、評価結果はfalseです。true === falseは一致しません。
  3. 次のcase score >= 80: に移ります。条件式は82 >= 80となり、評価結果はtrueです。
  4. true === trueは一致します!
  5. したがって、grade = '優';が実行され、breakswitch文を抜けます。

このように、switch (true)という形を使うと、caseラベルに複雑な条件式を書くことができ、特定の範囲による分岐を比較的読みやすく記述できます。これは、同じ値を異なるしきい値と比較するif...else ifの連鎖よりも、意図が伝わりやすい場合があります。

例:年齢によるカテゴリ分け

``javascript
function categorizeAge(age) {
let category;
switch (true) {
case age < 0:
category = '不正な年齢';
break;
case age < 13:
category = '子供';
break;
case age < 20:
category = 'ティーンエイジャー';
break;
case age < 65:
category = '成人';
break;
default:
category = 'シニア';
}
console.log(
年齢 ${age}: カテゴリ ‘${category}’`);
}

categorizeAge(5); // 年齢 5: カテゴリ ‘子供’
categorizeAge(17); // 年齢 17: カテゴリ ‘ティーンエイジャー’
categorizeAge(45); // 年齢 45: カテゴリ ‘成人’
categorizeAge(70); // 年齢 70: カテゴリ ‘シニア’
categorizeAge(-10); // 年齢 -10: カテゴリ ‘不正な年齢’
“`

このテクニックは強力ですが、通常のswitch文(単一値との比較)とは異なる考え方が必要となるため、チーム開発などで使用する際は、その意図がコードを読む人に明確に伝わるように配慮(コメントなど)すると良いでしょう。

2.5 スイッチ式内で計算を行う例

switch文の式には、計算式の結果を使うことも可能です。

``javascript
function checkNumberParity(num) {
let parity;
switch (num % 2) { // numを2で割った余りを評価 (0または1になる)
case 0:
parity = '偶数';
break;
case 1:
parity = '奇数';
break;
default:
// 数値でない場合など
parity = '不正な入力';
}
console.log(
${num}は${parity}です。`);
}

checkNumberParity(10); // 10は偶数です。
checkNumberParity(7); // 7は奇数です。
checkNumberParity(0); // 0は偶数です。
checkNumberParity(-3); // -3は奇数です。(JavaScriptの%演算子は負数の場合も負になる)
checkNumberParity(‘abc’); // abcは不正な入力です。 (非数値に対して%演算子を使うとNaNになる。NaN === 0, NaN === 1 はどちらもfalseなのでdefaultへ)
“`

この例では、num % 2という計算式の結果(0または1)をswitchの対象としています。これにより、数値が偶数か奇数かを判定しています。

2.6 定数(マジックナンバーを避ける)と組み合わせる

コードの中に直接数値や文字列のリテラルを書くことは、「マジックナンバー」(その値が何を意味するのか分かりにくい定数)として避けられることが多いです。特に、同じ値が何度も出てきたり、その値が将来変更される可能性がある場合は、定数として定義しておくことが推奨されます。

switch文のcaseラベルの値には、定数を使うことができます。

“`javascript
// 定義ファイルや定数ブロック
const STATUS_SUCCESS = 0;
const STATUS_BAD_REQUEST = 400;
const STATUS_NOT_FOUND = 404;
const STATUS_INTERNAL_ERROR = 500;
const STATUS_UNKNOWN = -1;

function processApiResponse(statusCode) {
let message;
switch (statusCode) {
case STATUS_SUCCESS: // case 0: と同じ意味だが、より分かりやすい
message = ‘API呼び出し成功。’;
break;
case STATUS_BAD_REQUEST: // case 400: と同じ意味
message = ‘クライアントエラー。リクエストを確認してください。’;
break;
case STATUS_NOT_FOUND: // case 404: と同じ意味
message = ‘APIエンドポイントが見つかりません。’;
break;
case STATUS_INTERNAL_ERROR: // case 500: と同じ意味
message = ‘サーバー側でエラーが発生しました。’;
break;
default:
message = ‘未知のAPIステータスコードです。’;
}
console.log(ステータスコード ${statusCode}: ${message});
}

processApiResponse(STATUS_SUCCESS); // ステータスコード 0: API呼び出し成功。
processApiResponse(STATUS_NOT_FOUND); // ステータスコード 404: APIエンドポイントが見つかりません。
processApiResponse(999); // ステータスコード 999: 未知のAPIステータスコードです。
“`

定数を使うことで、コードの意図が明確になり、保守性が向上します。例えば、もし将来STATUS_NOT_FOUNDの値が404から410に変更されたとしても、定数の定義箇所を一つ変更するだけで、switch文を含め、その定数を使っている全ての箇所が自動的に更新されます。

第3章: switch文の知っておくべき注意点と応用

switch文を使う上で、いくつかの重要な注意点や、さらに応用的な使い方があります。これらを理解することで、より堅牢で意図通りのコードを書くことができます。

3.1 厳密な比較 (===) が行われることの理解

繰り返しになりますが、switch文はswitch式の結果と各caseラベルの値を比較する際に、厳密等価演算子 (===) を使用します。これは非常に重要です。

===演算子は、値との両方が一致する場合にtrueを返します。型変換は一切行われません。

例:厳密な比較の挙動

“`javascript
let value = ‘1’; // 文字列型

switch (value) {
case 1: // 数値型 1 と比較
console.log(‘数値の1と一致’);
break;
case ‘1’: // 文字列型 ‘1’ と比較
console.log(‘文字列の\’1\’と一致’);
break;
default:
console.log(‘どちらとも一致しない’);
}
// 実行結果:
// 文字列の’1’と一致
“`

もしswitch (value)case 1:と比較されるとき、内部では'1' === 1という比較が行われます。これは値は同じように見えますが、型が異なる(文字列 vs. 数値)ため、結果はfalseとなります。

次のcase '1':では、'1' === '1'という比較が行われ、値も型も一致するため結果はtrueとなり、このブロックが実行されます。

この挙動は、特にユーザー入力など、値の型が確定しない場合や、数値と文字列が混在する場合に注意が必要です。意図したcaseブロックが実行されない場合は、switch式の値とcaseラベルの値の型が一致しているか確認しましょう。

3.2 caseラベルに使える値の型

caseラベルには、数値、文字列、真偽値、null、undefined、シンボルなどのプリミティブ値を指定できます。

“`javascript
let myVar = null;

switch (myVar) {
case 10:
console.log(‘数値’);
break;
case ‘hello’:
console.log(‘文字列’);
break;
case true:
console.log(‘真偽値’);
break;
case null: // nullも比較できる
console.log(‘null’);
break;
case undefined: // undefinedも比較できる
console.log(‘undefined’);
break;
default:
console.log(‘その他’);
}
// 実行結果:
// null
“`

オブジェクトや配列をcaseラベルの値として使うことも文法上は可能ですが、これは参照の比較になるため、通常は避けるべきです。

“`javascript
let obj = { a: 1 };
let arr = [1, 2];

switch (obj) {
case { a: 1 }: // このオブジェクトリテラルはobjとは異なる新しいオブジェクトなので、参照が一致しない
console.log(‘一致?’);
break;
case obj: // obj変数自身と比較
console.log(‘objと一致!’);
break;
default:
console.log(‘一致しない’);
}
// 実行結果:
// objと一致!
“`

case { a: 1 }: の部分は、objという変数自体が持っているオブジェクトへの参照ではなく、新しいオブジェクトリテラル{ a: 1 }をその場で作成し、その新しいオブジェクトへの参照がcaseの値となります。switch式の結果であるobjの参照と、新しいオブジェクトの参照は異なるため、一致しません。

caseラベルの値としてオブジェクトや配列を使うユースケースは非常に限定的であり、混乱を招きやすいため、基本的にはプリミティブ値を使うと覚えておいてください。

3.3 switch文内のスコープ

switch文全体は一つの大きなブロックのように見えますが、各caseラベルは厳密には独立したブロックスコープを作りません。しかし、モダンJavaScript(ES6以降)で導入されたletconstキーワードを使う場合、caseラベルの中で変数を宣言する際には注意が必要です。

もし複数のcaseブロック内で同じ名前の変数をletまたはconstで宣言しようとすると、エラーになります。

javascript
switch (value) {
case 1:
let x = 10; // OK
console.log(x);
break;
case 2:
// let x = 20; // エラー!case 1 と同じスコープレベルでxが宣言されていると見なされる
let y = 20; // OK
console.log(y);
break;
}

これを回避するためには、各caseブロックを明示的に波括弧 {} で囲んで、独立したブロックスコープを作成します。

javascript
switch (value) {
case 1: { // 波括弧でブロックスコープを作成
let x = 10;
console.log(x);
break;
}
case 2: { // 波括弧でブロックスコープを作成
let x = 20; // case 1 の x とは別の変数として宣言できる
console.log(x);
break;
}
}

varキーワードで宣言された変数は関数スコープを持つため、この問題は発生しませんが、モダンなJavaScriptではletconstの使用が推奨されます。したがって、switch文のcaseブロック内でletconstを使って変数を宣言する場合は、波括弧で囲む習慣をつけておくと安全です。

3.4 ネストされたswitch文

switch文の中に別のswitch文を記述する(ネストする)ことも可能です。

“`javascript
let status = ‘success’;
let subStatus = ‘completed’;

switch (status) {
case ‘success’:
console.log(‘ステータス: 成功’);
switch (subStatus) { // ネストされたswitch文
case ‘pending’:
console.log(‘サブステータス: 処理中’);
break;
case ‘completed’:
console.log(‘サブステータス: 完了’);
break;
default:
console.log(‘サブステータス: 不明’);
}
break; // 外側のswitchを抜けるためのbreak
case ‘error’:
console.log(‘ステータス: エラー’);
// エラー時のサブステータス処理など
break;
default:
console.log(‘ステータス: 不明’);
}
“`

ネストされたswitch文を使うと、より複雑な条件分岐を表現できます。しかし、ネストが深くなりすぎるとコードの可読性が著しく低下する可能性があります。一般的には、ネストは避けるか、浅いレベルに留めることが推奨されます。複雑すぎる場合は、関数に切り出すなどのリファクタリングを検討しましょう。

また、ネストされたswitch文内でbreak;を使用する場合、それは最も内側のswitch文から抜け出すだけで、外側のswitch文からは抜け出さない点に注意が必要です。外側のswitch文からも抜け出したい場合は、別途breakが必要になります(上記の例のように)。

3.5 switch文のパフォーマンスについて

switch文はif...else if文よりも高速である」という話を聞いたことがあるかもしれません。理論的には、多くのcaseラベルがある場合、switch文は内部的にジャンプテーブル(直接実行すべきコードの位置へ飛ぶ仕組み)を生成するなどの最適化が行われ、連続的な比較を行うif...else ifよりも効率的になる可能性があります。

しかし、現代のJavaScriptエンジンは非常に高度に最適化されており、ほとんどの実際のアプリケーションにおいて、switch文とif...else if文のパフォーマンスに体感できるほどの大きな差が出ることは稀です。パフォーマンスが問題になるのは、何百、何千といった非常に多くの条件分岐があり、それが極めてパフォーマンスクリティカルな処理の中にあるような、ごく限られた状況です。

したがって、通常はパフォーマンスを理由にswitch文を選ぶ必要はありません。どちらの構造を使うかは、コードの意図がより明確になり、読みやすく、保守しやすいかという観点で判断すべきです。

3.6 フォールスルーによるデバッグの難しさ

前述の通り、意図しないフォールスルーはswitch文でよくあるバグの原因です。そして、この種のバグはデバッグが少し難しい場合があります。なぜなら、エラーメッセージが出ないことが多く、コードの実行が単に意図した場所を超えて続いてしまうため、原因特定が難しいためです。

もしswitch文の挙動がおかしいと感じたら、まず以下の点を確認しましょう。

  1. switch式の結果と、一致するはずのcaseラベルの値が、値と型ともに厳密に一致 (===) しているか
  2. 一致したcaseブロックの最後にbreak;が記述されているか?意図的にフォールスルーさせている場合は、そのフォールスルーが想定通りか?

console.log()を各caseブロックの先頭や最後に仕込んで、どのブロックが実行されているかを確認するのが、フォールスルーのデバッグに有効な手段です。

第4章: switch文とif…else if文の比較と使い分け

switch文もif...else if文も、条件分岐のための構文です。どちらを使うべきか迷うことがあるかもしれません。ここでは、それぞれのメリット・デメリットを比較し、適切な使い分けのヒントを提供します。

4.1 switch文のメリット・デメリット

メリット:

  1. 単一値との多数の比較に優れる: 一つの値を複数の特定の値と比較する場合、if...else ifよりも簡潔で読みやすくなります。比較対象の値がswitchキーワードの直後に一度だけ書かれ、各caseラベルで比較対象の値を記述するだけなので、コードが整理されます。
  2. 可読性の向上 (特定のケース): 条件が多い場合でも、各caseラベルが明確な値を示すため、コードの意図が掴みやすいことがあります。
  3. フォールスルー: 複数の値に対して同じ処理を行いたい場合に、意図的なフォールスルーを使ってコードを簡潔に記述できます。

デメリット:

  1. 柔軟性の低さ: 比較対象が単一の値である場合に限定されます。複雑な条件式(例: x > 10 && y < 20)や、異なる変数間の比較には直接使えません(ただし、switch (true)のテクニックを使えばある程度カバーできます)。
  2. 範囲による比較が不得意: 特定の値との比較には向いていますが、「〇〇より大きい」「〇〇から△△の間」といった範囲による比較は、switch (true)を使わないと直感的に記述できません。
  3. 厳密な比較 (===): 型変換が行われない厳密な比較であるため、予期せぬ型の不一致に注意が必要です。
  4. break忘れ: breakを書き忘れると意図しないフォールスルーが発生し、バグの原因となりやすいです。

4.2 if…else if文のメリット・デメリット

メリット:

  1. 高い柔軟性: 任意の条件式を記述できます。比較演算子 (>, <, >=, <=, ==, !=, ===, !==) や論理演算子 (&&, ||, !) を自由に組み合わせて、複雑な条件を表現できます。
  2. 範囲による比較が得意: 「〇〇より大きい」「△△以下」といった範囲指定の条件分岐を自然に記述できます。
  3. 異なる変数間の比較が可能: 複数の変数を組み合わせた条件式を容易に記述できます。

デメリット:

  1. 冗長性: 一つの値を複数の特定の値と比較するようなケースで、比較対象の変数を繰り返し記述する必要があり、コードが冗長になりがちです。
  2. 条件が増えると読みにくくなる: 条件の数が増えるにつれて、else ifが長く連なり、どの条件が何に対応しているのか追いかけにくくなることがあります。

4.3 どちらを選ぶべきか?判断基準

これらのメリット・デメリットを踏まえると、switch文とif...else if文の使い分けは、主に条件分岐の性質によって決まります。

以下の基準を参考にしてみてください。

  • 比較対象が単一の値で、複数の特定の値のいずれかに一致するかどうかで分岐する場合:
    • switch文が適しています。コードが簡潔になり、可読性が高まります。例: ステータスコード、コマンド文字列、月の名前、曜日の名前など。
  • 条件が複雑な式である場合(範囲、論理演算子、異なる変数間の比較など):
    • if...else if文が適しています。switch文では表現が難しく、switch (true)を使う場合も意図が分かりにくいことがあります。例: 点数が〇〇以上かつ△△以下、ユーザーがログインしているかどうかとロールによる判定など。
  • 条件が少ない場合:
    • どちらを使っても大きな差はありません。より直感的に感じる方を選んで構いません。シンプルなif...elseで十分なことも多いです。
  • 可読性を重視する場合:
    • どちらの構文がよりコードの意図を明確に伝えるか、という観点で判断します。単一値との比較が多いならswitch、範囲や複雑な条件が多いならif...else ifが、一般的に可読性が高いとされます。ただし、これはあくまで一般的な傾向であり、具体的なコードによって異なります。
  • 意図的なフォールスルーが必要な場合:
    • switch文でしか実現できません。複数のcaseをまとめる場合に有効です。

簡単なフローチャート風の判断基準:

  1. 分岐したい条件は、ある「単一の値」と「複数の特定の値」の比較か?
    • はい → switch文を検討(厳密な比較に注意)
    • いいえ → if...else if文を検討
  2. 単一値との比較だが、範囲判定や複雑な条件式が多いか?
    • はい → if...else if文か、switch(true)テクニックを検討。どちらが読みやすいか比較する。
    • いいえ → switch文が有力候補。
  3. 複数の値で同じ処理をしたいか?
    • はい → switch文でフォールスルーを使うと簡潔になる可能性がある。
    • いいえ → 他の基準で判断。
  4. 条件の数は非常に多いか?
    • switch文は見た目が整理されやすい。ただし、非常に多い場合は、switch文自体が長くなりすぎる可能性も。その場合は、後述するルックアップテーブルなども検討する。

4.4 リファクタリングの例

既存のコードで、if...else if文とswitch文を相互にリファクタリング(より良いコードに書き換えること)する例を見てみましょう。

例1: 冗長なif…else ifをswitchに

“`javascript
// 元のコード (冗長なif…else if)
let animal = ‘cat’;
let sound;

if (animal === ‘dog’) {
sound = ‘ワンワン’;
} else if (animal === ‘cat’) {
sound = ‘ニャーニャー’;
} else if (animal === ‘cow’) {
sound = ‘モーモー’;
} else {
sound = ‘???’;
}
console.log(${animal}の鳴き声: ${sound});

// switchにリファクタリング
switch (animal) {
case ‘dog’:
sound = ‘ワンワン’;
break;
case ‘cat’:
sound = ‘ニャーニャー’;
break;
case ‘cow’:
sound = ‘モーモー’;
break;
default:
sound = ‘???’;
}
console.log(${animal}の鳴き声: ${sound});
“`

このケースでは、switch文の方が比較対象であるanimalが最初に明確に示され、各caseで値だけを記述すれば良いため、コードが短くなり、可読性が向上します。

例2: 複雑なswitch(true)をif…else ifに(可読性のため)

``javascript
// 元のコード (switch(true)で範囲判定)
function getTemperatureComment(temp) {
let comment;
switch (true) {
case temp < 0:
comment = '凍える寒さ!';
break;
case temp < 10:
comment = 'かなり寒い。';
break;
case temp < 20:
comment = '少し肌寒い。';
break;
case temp < 30:
comment = '快適な気温。';
break;
default:
comment = '暑い!';
}
return comment;
}
console.log(
気温5度: ${getTemperatureComment(5)}`); // 気温5度: かなり寒い。

// if…else ifにリファクタリング
function getTemperatureCommentRefactored(temp) {
let comment;
if (temp < 0) {
comment = ‘凍える寒さ!’;
} else if (temp < 10) {
comment = ‘かなり寒い。’;
} else if (temp < 20) {
comment = ‘少し肌寒い。’;
} else if (temp < 30) {
comment = ‘快適な気温。’;
} else {
comment = ‘暑い!’;
}
return comment;
}
console.log(気温5度: ${getTemperatureCommentRefactored(5)}); // 気温5度: かなり寒い。
“`

このケースでは、どちらのコードでも同じ結果が得られますが、温度の範囲によってコメントを変えるという意図は、if...else if文の方がより直接的に表現されていると感じる人が多いかもしれません。switch(true)は強力なテクニックですが、それがコードの意図を読みにくくする場合は、従来のif...else ifの方が適切なこともあります。

リファクタリングは、単に構文を置き換えるだけでなく、そのコードの目的可読性保守性を考慮して行うべきです。

第5章: モダンJavaScriptにおける代替手段とswitch文の立ち位置

JavaScriptのエコシステムは進化し続けており、switch文を使う代わりに、よりモダンなアプローチが適しているケースもあります。ここでは、オブジェクトやMapを使ったルックアップテーブルという代替手段を紹介し、それらと比較した場合のswitch文の立ち位置を考察します。

5.1 オブジェクトやMapを使ったルックアップテーブル

多数の特定の入力値に対して決まった出力値や処理がある場合、switch文の代わりにオブジェクトMapを「ルックアップテーブル」として使用する手法があります。

例:コマンドに応じた処理をオブジェクトで管理

先ほどのコマンド処理の例を、オブジェクトを使って書き換えてみましょう。

“`javascript
const commandActions = {
‘open’: () => console.log(‘ファイルを開きます。’),
‘save’: () => console.log(‘ファイルを保存します。’),
‘close’: () => console.log(‘ファイルを閉じます。’),
‘print’: () => console.log(‘ファイルを印刷します。’)
};

function handleCommandWithObject(command) {
const action = commandActions[command.toLowerCase()]; // コマンド文字列をキーとしてアクション(関数)を取得
if (action) {
action(); // 取得したアクション(関数)を実行
} else {
console.log(不明なコマンド '${command}' です。);
}
}

handleCommandWithObject(‘Open’); // ファイルを開きます。
handleCommandWithObject(‘Save’); // ファイルを保存します。
handleCommandWithObject(‘delete’); // 不明なコマンド ‘delete’ です。
“`

このコードのメリットは以下の通りです。

  • 簡潔さ: switch/case/breakといったキーワードが不要になり、非常にシンプルに書けます。
  • データの分離: 入力値とそれに対応する処理(この場合は関数)が、commandActionsという一つのオブジェクトにまとまっており、コード本体から分離されています。これにより、新しいコマンドを追加したり、既存のコマンドの処理を変更したりするのが容易になります。
  • 動的なキー: オブジェクトのキーは動的に生成することも可能です。switch文のcaseラベルは静的な値である必要がありますが、オブジェクトのプロパティアクセスは変数など動的なキーを使えます。
  • 柔軟な値: オブジェクトの値には、関数だけでなく、文字列、数値、別のオブジェクトなど、何でも格納できます。

同様のことはMapオブジェクトでも実現できます。Mapはオブジェクトよりも柔軟なキー(数値やオブジェクトなどもキーにできる)を持つことができるため、より多様なルックアップテーブルを作成できます。

例:エラーコードに応じたメッセージをMapで管理

“`javascript
const errorMessages = new Map([
[0, ‘処理が正常に完了しました。’],
[400, ‘リクエストが不正です。’],
[404, ‘指定されたリソースが見つかりません。’],
[500, ‘サーバー内部エラーが発生しました。’]
]);

function processErrorCodeWithMap(errorCode) {
const message = errorMessages.get(errorCode); // errorCodeをキーとしてメッセージを取得
if (message !== undefined) { // undefinedと比較して、キーが存在したか確認
console.log(エラーコード ${errorCode}: ${message});
} else {
console.log(不明なエラーコード ${errorCode} です。);
}
}

processErrorCodeWithMap(0); // エラーコード 0: 処理が正常に完了しました。
processErrorCodeWithMap(404); // エラーコード 404: 指定されたリソースが見つかりません。
processErrorCodeWithMap(999); // 不明なエラーコード 999 です。
“`

Mapを使ったルックアップテーブルも、switch文と同様の目的を達成しつつ、コードをよりデータ駆動型にし、整理することができます。

5.2 switch文 vs. ルックアップテーブル

オブジェクトやMapによるルックアップテーブルは、特定の状況ではswitch文の優れた代替となります。では、どちらを選ぶべきでしょうか?

  • ルックアップテーブルが有利な場合:
    • 多くの特定の入力値: 条件の数が非常に多い場合、ルックアップテーブルの方がコードがコンパクトになり、管理が容易になります。
    • 入力値と出力値/処理が静的に決まっている: 入力値に対して対応する出力や処理が単純に決まっている場合に適しています。
    • 動的なキーが必要な場合: switch文のcaseは静的な値でなければなりませんが、オブジェクト/Mapは動的なキーを使えます。
    • データとロジックを分離したい: ルックアップテーブルは、分岐の「データ」(どの入力値に何が対応するか)をコード本体から切り離すのに役立ちます。
  • switch文が有利な場合:
    • フォールスルーが必要な場合: 複数の値に対して同じ処理をしたい場合に、switch文のフォールスルーは自然な記述方法です。ルックアップテーブルでは、複数のキーに対して同じ値を設定するか、キーを配列として扱うなどの工夫が必要になります。
    • 範囲判定など、switch(true)が有効な場合: 複雑な条件式を使いたいが、if...else ifが冗長になる、かつswitch(true)のパターンが読みやすいと感じる場合。
    • 比較対象の値の型が多様な場合: ルックアップテーブルのキーには制約がある場合があります(オブジェクトのキーは文字列かSymbolになる)。switchはプリミティブ値であれば比較的柔軟に扱えます。
    • 条件の数がそれほど多くない場合: 条件が数個程度であれば、switch文も十分に読みやすく、ルックアップテーブルを作る手間が不要なため、シンプルに書けます。

結局のところ、これもまたコードの意図を最も明確に表現でき、将来の変更に最も容易に対応できる構造を選ぶことが重要です。モダンなJavaScript開発では、データ構造(オブジェクトやMap)を活用して条件分岐を表現するパターンも一般的になってきており、コードをより宣言的に、データ駆動型にする流れがあります。しかし、switch文が依然として多くの場面で有効であり、特にフォールスルーのような独自の機能は代替手段では容易に実現できません。

5.3 ポリモーフィズムによる代替(オブジェクト指向的な視点)

さらにオブジェクト指向的な視点で見ると、条件分岐を減らすための一つのアプローチとして「ポリモーフィズム」があります。これは、「様々な型のオブジェクトが、同じメッセージを受け取ったときに、その型に応じた異なる振る舞いをする」という概念です。

例えば、様々な種類の図形オブジェクト(円、四角、三角形など)があり、それぞれを描画する処理を行いたいとします。switch文やif...else ifを使うと、図形の種類に応じて描画処理を分岐させることになります。

javascript
// 条件分岐によるアプローチ
function drawShape(shape) {
switch (shape.type) {
case 'circle':
drawCircle(shape);
break;
case 'square':
drawSquare(shape);
break;
case 'triangle':
drawTriangle(shape);
break;
default:
console.error('不明な図形タイプ:', shape.type);
}
}

ポリモーフィズムを利用する場合、各図形オブジェクト自身が描画メソッドを持つように設計します。

``javascript
// ポリモーフィズムによるアプローチ
class Circle {
constructor(radius) { this.radius = radius; }
draw() { console.log(
円を描画します (半径: ${this.radius})`); }
}

class Square {
constructor(side) { this.side = side; }
draw() { console.log(四角形を描画します (一辺: ${this.side})); }
}

class Triangle {
constructor(base, height) { this.base = base; this.height = height; }
draw() { console.log(三角形を描画します (底辺: ${this.base}, 高さ: ${this.height})); }
}

const shapes = [
new Circle(5),
new Square(10),
new Triangle(6, 8)
];

shapes.forEach(shape => {
shape.draw(); // 各オブジェクトが自身のdrawメソッドを呼び出す
});
// 実行結果:
// 円を描画します (半径: 5)
// 四角形を描画します (一辺: 10)
// 三角形を描画します (底辺: 6, 高さ: 8)
“`

このポリモーフィズムを使ったアプローチでは、drawShapeのような大きな条件分岐は不要になります。新しい図形タイプ(例: 楕円)を追加したい場合も、新しいクラスを作成してdrawメソッドを実装するだけで、既存のshapes.forEachのループ側を変更する必要はありません。これは、「オープン/クローズドの原則」(拡張に対しては開いており、修正に対しては閉じているべき) に従った設計であり、大規模なシステム開発においては非常に強力なパターンです。

全ての条件分岐をポリモーフィズムに置き換えるべきではありませんが、オブジェクトの種類によって振る舞いを分けたい、あるいは将来的に新しい種類が頻繁に追加される可能性がある、といったケースでは、switch文やif...else if文に代わる有力な選択肢として考慮する価値があります。

第6章: 実践的なヒントとベストプラクティス

switch文を実際のコードで使う際に役立つヒントと、推奨される書き方(ベストプラクティス)をいくつか紹介します。

6.1 常にdefaultケースを含めるべきか?

defaultケースは省略可能ですが、含めることが推奨されることが多いです。その理由は以下の通りです。

  • 予期しない値への対応: プログラムが受け取る入力値は、常に想定通りとは限りません。外部からの入力、APIからの応答、計算結果など、どのcaseにも一致しない値が渡される可能性があります。defaultケースを用意しておくことで、そのような予期しない値が渡された場合でもプログラムがクラッシュすることなく、エラーメッセージを表示したり、デフォルトの処理を実行したり、といった対応が可能になります。
  • デバッグ: どのcaseにも一致しなかった場合にdefaultが実行されることで、渡された値が想定外のものであることを検知できます。
  • 網羅性: switch文が扱いうる全ての可能性を考慮していることを示す意図でdefaultを含めることがあります。

ただし、全ての可能な入力値がcaseラベルとして明示的にリストされており、それ以外の値が渡される可能性がゼロであると確信できる場合は、defaultを省略することもあります。例えば、列挙型(Enum)のような固定された値のみを扱う場合などです。しかし、このようなケースは実際には少なく、入力値が少しでも不確実であればdefaultを含める方が安全です。

defaultを含める場合、それが単にエラーを報告するだけなのか、それとも正常な処理の一部としてデフォルト値を設定するのか、その目的を明確にすることが重要です。

6.2 breakを忘れないための習慣とコードレビュー

意図しないフォールスルーを防ぐためには、各caseブロックの最後にbreak;を書くことを習慣づけるのが最も重要です。

  • 書き方の習慣: case 値: と書いたら、すぐにその下にbreak;を書き、その間に実行したい処理を書く、という習慣をつけるのが効果的です。
  • IDEやエディターの活用: 多くのモダンなIDEやコードエディターは、構文エラーや潜在的な問題(breakがないなど)を警告してくれます。これらの機能を活用しましょう。
  • リンター (ESLintなど): ESLintのようなJavaScriptリンターツールを設定すると、switch文におけるbreakの欠落を警告したり、特定のスタイル(例えば、フォールスルーする場合はコメントを強制するなど)を強制したりできます。チーム開発では特にリンターの導入が推奨されます。
  • コードレビュー: チームで開発している場合は、コードレビューでお互いのコードを確認し合い、breakの欠落や意図しないフォールスルーがないかチェックすることは、バグの早期発見に非常に有効です。

6.3 可読性を高めるためのインデントとコメント

switch文は、その構造上、適切なインデント(字下げ)が非常に重要です。各caseラベルと、その中の処理ブロック、そしてbreak;は、それぞれ適切にインデントすることで、どのコードがどのcaseに属しているかが一目で分かります。

“`javascript
// 良い例: 適切なインデント
switch (status) {
case ‘pending’:
console.log(‘処理中です…’);
// 複雑な処理…
break;
case ‘completed’:
console.log(‘完了しました!’);
// 別の処理…
break;
default:
console.log(‘不明なステータス’);
}

// 悪い例: インデントが不適切だと読みにくい
switch (status) {
case ‘pending’:
console.log(‘処理中です…’);
break;
case ‘completed’:
console.log(‘完了しました!’);
break;
default:
console.log(‘不明なステータス’);
}
“`

モダンな開発環境では、PrettierやESLintなどのコードフォーマッター/リンターが自動的にインデントを修正してくれるため、これらを導入することでコードスタイルを統一し、可読性を保つことができます。

また、意図的にフォールスルーを使用する場合は、その意図を明確にするためにコメントを記述することが推奨されます。

javascript
switch (dayOfWeek) {
case '土':
case '日': // <-- 意図的なフォールスルー (週末としてまとめて処理)
console.log('週末を楽しもう!');
break;
// ...他のケース
}

6.4 マジックナンバーを避ける(定数化)

第2章でも触れましたが、caseラベルに直接リテラル値を記述する代わりに、その値の意味を明確に表す定数を使用することで、コードの意図がより伝わりやすくなります。特に、アプリケーション全体で共通して使われるステータスコード、設定値、エラーコードなどの場合は、定数として定義し、それを使用することが強く推奨されます。

“`javascript
// const.js など別のファイルで定義
export const USER_ROLES = {
ADMIN: ‘admin’,
EDITOR: ‘editor’,
VIEWER: ‘viewer’
};

// 別のファイルで使用
import { USER_ROLES } from ‘./const’;

function checkPermissions(userRole) {
switch (userRole) {
case USER_ROLES.ADMIN: // ‘admin’ という文字列より意図が明確
console.log(‘管理者権限があります。’);
// 管理者向けの処理…
break;
case USER_ROLES.EDITOR: // ‘editor’ という文字列より意図が明確
console.log(‘編集権限があります。’);
// 編集者向けの処理…
break;
case USER_ROLES.VIEWER: // ‘viewer’ という文字列より意図が明確
console.log(‘閲覧権限があります。’);
// 閲覧者向けの処理…
break;
default:
console.log(‘権限がありません。’);
}
}
“`

6.5 大規模なswitch文の管理

もしcaseラベルの数が非常に多くなり、switch文自体が数百行、あるいはそれ以上の長さになる場合、そのコードは読みにくく、保守が難しくなる可能性があります。このような場合は、switch文以外の構造を検討するべきサインかもしれません。

代替手段としては、前述のルックアップテーブル(オブジェクトやMap)、あるいは各caseブロックの処理を別の関数に切り出すといった方法があります。

“`javascript
// 長大なswitch文(イメージ)
switch (command) {
case ‘cmd1’:
// cmd1の処理(数十行)
break;
case ‘cmd2’:
// cmd2の処理(数十行)
break;
// … cmdNまで続く
}

// 関数に切り出す例
function processCommand(command) {
switch (command) {
case ‘cmd1’:
handleCmd1(); // 関数呼び出し
break;
case ‘cmd2’:
handleCmd2(); // 関数呼び出し
break;
// …
default:
handleUnknownCommand();
}
}

function handleCmd1() {
// cmd1の実際の処理
}
// …他のハンドラ関数
“`

さらに、ルックアップテーブルと関数を組み合わせることも可能です。

“`javascript
const commandHandlers = {
‘cmd1’: handleCmd1,
‘cmd2’: handleCmd2,
// …
‘cmdN’: handleCmdN
};

function processCommandWithHandlers(command) {
const handler = commandHandlers[command];
if (handler) {
handler();
} else {
handleUnknownCommand();
}
}
“`

このように、状況に応じて適切な構造を選択することで、コードの複雑さを管理し、可読性と保守性を維持することができます。

第7章: デバッグとトラブルシューティング

switch文に関連するバグは、主にbreak忘れによる意図しないフォールスルーか、switch式の結果とcaseラベルの値の型の不一致によるものです。これらの問題をデバッグするための一般的な方法を見ていきましょう。

7.1 console.logを使ったswitch文のデバッグ

最も基本的なデバッグ方法は、console.log()を使ってプログラムの実行フローや変数の値を確認することです。switch文のデバッグでは、以下の箇所にconsole.logを仕込むのが有効です。

  1. switch式の直前: 評価される値が何か、その型は何かに間違いがないか確認します。
  2. caseブロックの先頭: どのcaseブロックに実行が到達したかを確認します。
  3. caseブロックの最後(breakの前): 処理が最後まで実行されたか確認します。breakがない場合は、そのまま次のconsole.logが実行されるはずです。
  4. defaultブロックの先頭: どのcaseにも一致しなかったか確認します。
  5. switch文の直後: switch文を抜けた後に期待通りの処理が行われているか確認します。

例:

“`javascript
let statusValue = 1; // デバッグしたい値

console.log(‘— switch文開始 —‘);
console.log(‘switch式の評価対象:’, statusValue, typeof statusValue); // 評価対象と型を確認

switch (statusValue) {
case 0:
console.log(‘-> case 0 に到達’);
// 処理…
console.log(‘-> case 0 終了’);
break; // ここで止まるか?
case 1:
console.log(‘-> case 1 に到達’); // ここに到達するか?
// 処理…
console.log(‘-> case 1 終了’);
// break がない場合、次の行に進む
case 2:
console.log(‘-> case 2 に到達’); // フォールスルーした場合、ここに到達する
// 処理…
console.log(‘-> case 2 終了’);
break;
default:
console.log(‘-> default に到達’); // どのケースにも一致しなかった場合に到達
console.log(‘-> default 終了’);
}

console.log(‘— switch文終了 —‘);
“`

このconsole.logの出力を見ることで、どのcaseブロックが実行され、どこでフォールスルーが発生しているか、あるいはなぜどのcaseにも一致しないのか(defaultに到達しているか)などを追跡できます。

7.2 フォールスルーによる予期せぬ挙動の特定

前述のconsole.logを使ったデバッグは、フォールスルーの特定に特に有効です。意図しないフォールスルーが発生している場合、期待していなかったcaseブロックの先頭を示すconsole.logが出力されているのを見て、breakが欠落している箇所を特定できます。

例えば、case 1の最後にbreakがない状態でstatusValue1の場合、デバッグ出力は以下のようになるでしょう。

--- switch文開始 ---
switch式の評価対象: 1 number
-> case 1 に到達
-> case 1 終了
-> case 2 に到達 // ここが出力されたらフォールスルーを疑う
-> case 2 終了
--- switch文終了 ---

ここで、「-> case 2 に到達」というログは本来出てほしくなかったものです。case 1 終了の直後にcase 2 に到達が来ていることから、case 1の最後にbreakがないことが原因だと推測できます。

7.3 比較対象の値や型を確認する

switch文が期待通りに動作しない別の主な原因は、===による厳密な比較で値や型が一致しないことです。

この場合も、console.log()を使って、switch式の評価結果と、各caseラベルの値をログ出力して比較するのが効果的です。特にtypeof演算子を使って型を確認することは重要です。

“`javascript
let idInput = prompt(‘IDを入力してください:’); // promptは常に文字列を返す

console.log(‘ID入力値:’, idInput, typeof idInput); // idInputとその型を確認

switch (idInput) {
case 100: // 数値の100
console.log(‘数値100と一致’);
break;
case ‘100’: // 文字列の’100′
console.log(‘文字列\’100\’と一致’);
break;
default:
console.log(‘一致しない’);
}
“`

もしユーザーが100と入力した場合、idInputの値は文字列の'100'になります。

ID入力値: 100 string // ここで型がstringであることがわかる
一致しない // '100' === 100 は false なのでこちらになる

この出力を見て、「あれ?case 100があるのに一致しないぞ?」と思ったときに、idInputが数値ではなく文字列であることがログから分かるため、原因が型の不一致にあるとすぐに特定できます。

まとめ:switch文を使いこなし、より良いコードを書こう

この記事では、JavaScriptのswitch文について、その基本的な構文から始まり、具体的な使用例、知っておくべき注意点、if...else if文との比較、代替手段、そしてデバッグ方法に至るまで、幅広く詳細に解説しました。

switch文は、ある一つの値を複数の特定の値と比較して処理を分岐させたい場合に、コードをより簡潔で、整理され、読みやすいものにする強力なツールです。特に、ステータスコードの処理、コマンドのディスパッチ、列挙型のような固定値に対する処理などでその真価を発揮します。

しかし、switch文は万能ではありません。複雑な条件式や範囲による分岐にはif...else if文の方が適していることが多いですし、条件の数が非常に多い場合や動的なキーが必要な場合は、オブジェクトやMapを使ったルックアップテーブルの方が優れた代替手段となることもあります。

重要なのは、それぞれの条件分岐構文の特徴と得意な状況を理解し、目の前の問題に対して最も適切で、可読性が高く、保守しやすいコードを選択することです。

この記事を通して、あなたは以下の点を理解できたはずです。

  • switch, case, break, defaultの各キーワードの役割と使い方。
  • breakの重要性、そして意図的なフォールスルーと意図しないフォールスルーの違い。
  • 数値、文字列、複数のケースをまとめるフォールスルー、そしてswitch(true)を使った範囲判定などの具体的な活用例。
  • switch文が厳密な比較 (===) を行うことの注意点。
  • switch文内のスコープの扱い方 ({}の利用)。
  • if...else if文との使い分けの判断基準。
  • オブジェクト/Mapによるルックアップテーブルといった代替手段とその比較。
  • defaultケースを含めることの推奨、break忘れ防止策、適切なインデントやコメント、定数化といったベストプラクティス。
  • console.logを使ったデバッグ方法。

これらの知識を活かして、あなたのJavaScriptコードにおける条件分岐を、より効果的で分かりやすいものにしてください。

さあ、今日からあなたのコードでswitch文を自信を持って活用しましょう!そして、もし迷ったときは、この記事で学んだ知識を参考に、状況に応じて最適な条件分岐を選んでください。

コメントする

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

上部へスクロール