Javascript if 複数条件の使い方を徹底解説

JavaScript if文 複数条件の使い方を徹底解説

JavaScriptにおいて、プログラムの流れを制御するために最も頻繁に使用される構文の一つが if 文です。特定の条件が真である場合にのみ、特定のコードブロックを実行することができます。しかし、実際のアプリケーション開発では、単一の条件だけでは不十分な場面がほとんどです。複数の条件を組み合わせて判断する必要が頻繁に発生します。

この記事では、JavaScriptの if 文で複数の条件を扱う方法に焦点を当て、その基本的な使い方から、より複雑なケース、さらには可読性やパフォーマンスといった実践的な側面まで、徹底的に解説します。約5000語にわたる詳細な説明を通じて、JavaScriptにおける条件分岐の奥深さを理解し、より堅牢で効率的なコードを書けるようになることを目指します。

1. はじめに: プログラムの流れを制御する if 文と複数条件の必要性

プログラムは基本的に上から下へと順番に処理を実行していきます。しかし、現実世界の複雑な問題に対処するためには、状況に応じて異なる処理を選択する必要があります。例えば、「ユーザーがログインしているかつ管理者権限を持っている場合にのみ、管理画面を表示する」といった判断は、複数の条件が同時に満たされるかどうかを確認しないと行えません。

JavaScriptの if 文は、このような条件に基づいた処理の分岐を実現するための基本的な構文です。

javascript
if (条件式) {
// 条件式が真 (true) と評価された場合に実行されるコードブロック
}

ここで 条件式 には、真(true)または偽(false)と評価される式を記述します。比較演算子(==, ===, !=, !==, <, >, <=, >=)や論理演算子(&&, ||, !)などを使用して、複雑な条件を表現できます。

単一の条件式で事足りる場面も多いですが、多くのシナリオでは以下のような複数の条件を組み合わせた判断が必要になります。

  • AND条件: 「Aも真であるかつBも真である」場合に処理を実行したい。
  • OR条件: 「Aが真であるまたはBが真である」場合に処理を実行したい。
  • NOT条件: 「Aが真であるではない」場合に処理を実行したい。
  • これらの組み合わせ:「Aが真であるかつ(Bが真であるまたはCが真である)」といったより複雑な論理。

この記事では、これらの複数の条件を if 文の中でどのように記述し、どのように制御するのかを詳しく見ていきます。

2. if 文の基本: 単一条件の復習

複数条件に進む前に、まずは単一条件の if 文の基本を復習しておきましょう。

最も基本的な形式は if です。

“`javascript
let age = 20;

if (age >= 20) {
console.log(“あなたは成人です。”);
}
“`

この例では、変数 age の値が20以上であるかどうかが評価されます。age が20なので、条件式 age >= 20true となり、ブロック内のコード console.log("あなたは成人です。"); が実行されます。

条件が偽 (false) だった場合に別の処理を実行したい場合は、else ブロックを使用します。

“`javascript
let age = 18;

if (age >= 20) {
console.log(“あなたは成人です。”);
} else {
console.log(“あなたは未成年です。”);
}
“`

この場合、age は18なので age >= 20false となります。したがって if ブロックはスキップされ、else ブロック内のコード console.log("あなたは未成年です。"); が実行されます。

さらに、複数の候補の中から一つを選びたい場合は、else if を使って条件を追加できます。

“`javascript
let score = 85;

if (score >= 90) {
console.log(“評価:A”);
} else if (score >= 80) {
console.log(“評価:B”);
} else if (score >= 70) {
console.log(“評価:C”);
} else {
console.log(“評価:D”);
}
“`

if-else if-else の構造では、条件式は上から順番に評価されます。最初の条件式が真と評価された時点で、そのブロック内のコードが実行され、以降の else ifelse の条件は評価されることなく、一連の if 文の処理は終了します。どの ifelse if も真にならなかった場合にのみ、最後の else ブロックが実行されます(else は省略可能です)。

この if-else if-else 構造は、複数の候補の中から一つを選ぶという点で、後述する switch 文と似ていますが、if-else if は任意の条件式を記述できるのに対し、switch 文は主に単一の値と複数の候補値を比較する等価比較に使用されるという違いがあります。

これらの基本的な if 文の使い方は、複数条件を扱う場合でも同様ですが、条件式の中に複数の条件を組み合わせて記述する方法を学ぶ必要があります。

3. 複数条件の表現: 論理演算子の活用

複数の条件を組み合わせるためには、JavaScriptが提供する論理演算子を使用します。論理演算子は、真(true)または偽(false)の値をオペランドとして受け取り、真(true)または偽(false)の値を返します。

主要な論理演算子は以下の3つです。

  • && (論理AND)
  • || (論理OR)
  • ! (論理NOT)

それぞれの使い方を詳しく見ていきましょう。

3.1. && (論理AND)

論理AND演算子 && は、左辺と右辺の両方のオペランドが真と評価された場合にのみ、結果が真となります。それ以外の場合は、結果は偽となります。

例えば、「ユーザーがログインしているかつ管理者権限を持っている」という条件を表現する場合に使用します。

“`javascript
let isLoggedIn = true;
let hasAdminAccess = true;

if (isLoggedIn && hasAdminAccess) {
console.log(“管理画面にアクセスできます。”);
} else {
console.log(“管理画面にアクセスできません。”);
}
“`

この例では、isLoggedIntruehasAdminAccesstrue なので、条件式 isLoggedIn && hasAdminAccesstrue && true と評価され、結果は true となります。したがって、if ブロック内のコードが実行されます。

もし isLoggedInfalse だった場合、または hasAdminAccessfalse だった場合、あるいはその両方が false だった場合、条件式の結果は false となり、else ブロックのコードが実行されます。

真理値表 (AND):

オペランドA オペランドB A && B
true true true
true false false
false true false
false false false

AND条件は、「〜かつ〜」というように、複数の条件が同時に満たされる必要がある場合に使われます。

3.2. || (論理OR)

論理OR演算子 || は、左辺または右辺のいずれか一方、または両方のオペランドが真と評価された場合に、結果が真となります。両方のオペランドが偽と評価された場合にのみ、結果は偽となります。

例えば、「週末であるまたは祝日である」場合に特別な処理を実行したい、という条件を表現する場合に使用します。

“`javascript
let isWeekend = false;
let isHoliday = true;

if (isWeekend || isHoliday) {
console.log(“特別なイベントを実施します。”);
} else {
console.log(“通常営業です。”);
}
“`

この例では、isWeekendfalseisHolidaytrue なので、条件式 isWeekend || isHolidayfalse || true と評価され、結果は true となります。したがって、if ブロック内のコードが実行されます。

もし isWeekendtrue だった場合(true || truetrue || false)、結果は true となります。両方が falsefalse || false)の場合にのみ、結果は false となり、else ブロックのコードが実行されます。

真理値表 (OR):

| オペランドA | オペランドB | A || B |
| :——— | :——— | :—– |
| true | true | true |
| true | false | true |
| false | true | true |
| false | false | false |

OR条件は、「〜または〜」というように、複数の条件のうち少なくとも一つが満たされれば良い場合に使われます。

3.3. ! (論理NOT)

論理NOT演算子 ! は、オペランドの真偽値を反転させます。真(true)であれば偽(false)に、偽(false)であれば真(true)に変換します。オペランドは一つだけです。

例えば、「ユーザーがログインしていない」という条件を表現する場合に使用します。

“`javascript
let isLoggedIn = false;

if (!isLoggedIn) {
console.log(“ログインしてください。”);
} else {
console.log(“ようこそ!”);
}
“`

この例では、isLoggedInfalse なので、条件式 !isLoggedIn!false と評価され、結果は true となります。したがって、if ブロック内のコードが実行されます。

isLoggedIntrue の場合、!isLoggedIn!true と評価され、結果は false となります。この場合、else ブロックのコードが実行されます。

真理値表 (NOT):

オペランドA !A
true false
false true

NOT演算子は、特定の条件が満たされない場合に処理を行いたい場合に使われます。

3.4. 複数の論理演算子を組み合わせる

これらの論理演算子を複数組み合わせて、より複雑な条件を表現することができます。

例えば、「ユーザーがログインしており、かつ(管理者であるか編集者であるか)」という条件を考えてみましょう。これは「isLoggedIn && (isAdmin || isEditor)」と表現できます。

“`javascript
let isLoggedIn = true;
let isAdmin = false;
let isEditor = true;

if (isLoggedIn && (isAdmin || isEditor)) {
console.log(“コンテンツを編集できます。”);
} else {
console.log(“コンテンツを編集できません。”);
}
“`

この例では、isLoggedIntrueisAdminfalseisEditortrue です。
まず括弧の中の isAdmin || isEditor が評価されます。これは false || true なので true となります。
次に isLoggedIn && (true) が評価されます。これは true && true なので true となります。
したがって、条件式全体は true となり、if ブロックが実行されます。

3.5. 評価の順序と短絡評価 (Short-Circuit Evaluation)

複数の論理演算子を含む条件式は、JavaScriptの演算子の優先順位に従って評価されます。一般的に、!&& よりも優先順位が高く、&&|| よりも優先順位が高いです。

  • ! (NOT)
  • && (AND)
  • || (OR)

優先順位が同じ場合は、通常左から右へ評価されます。ただし、後述する括弧 () を使用することで、評価の順序を明示的に制御できます。

例:A || B && CA || (B && C) と評価されます。一方、(A || B) && C は括弧が優先されます。

短絡評価 (Short-Circuit Evaluation) は、論理演算子の重要な特性です。条件式全体の真偽値が、式の一部を評価しただけで確定する場合、JavaScriptはその時点で評価を停止します。

  • && (AND): 左辺が false と評価された場合、右辺は評価されません。なぜなら、左辺が偽であれば、右辺が真であろうと偽であろうと、&& の結果は必ず偽になるからです (false && anything は常に false)。
  • || (OR): 左辺が true と評価された場合、右辺は評価されません。なぜなら、左辺が真であれば、右辺が真であろうと偽であろうと、|| の結果は必ず真になるからです (true || anything は常に true)。

短絡評価は、特に以下のような場合に役立ちます。

  1. エラー回避: 右辺の評価が高価な処理を含んでいたり、特定の条件が満たされないとエラーが発生する可能性がある場合に、左辺でその条件をチェックすることでエラーを防ぐことができます。

    javascript
    let user = null;
    // userがnullでない かつ user.profileが存在する場合にアクセス
    if (user !== null && user.profile !== undefined) {
    console.log(user.profile.name); // userがnullなら&&の時点でfalseになり、user.profileへのアクセスは発生しない
    }

    最近のJavaScriptではオプショナルチェイニング ?. を使う方が一般的で安全ですが、短絡評価の古典的な例として理解しておくことは重要です。

  2. パフォーマンス向上: コストのかかる処理を条件式の右辺に置き、左辺で簡単な条件をチェックすることで、不要な処理の実行をスキップできます。

    javascript
    // まず簡単なチェックを行い、必要なら時間のかかるDB問い合わせを行う関数を呼ぶ
    if (isCacheValid || fetchDataFromDatabase()) {
    // ... データの処理 ...
    }

    isCacheValidtrue であれば、fetchDataFromDatabase() 関数は呼び出されません。

  3. 条件付き代入: 短絡評価を利用して、条件に応じて値を代入する簡潔な記述が可能です。

    javascript
    let name = user && user.name; // userが真 (null, undefined以外) なら user.name を代入
    let greeting = userName || "ゲスト"; // userNameが真 (空文字列以外) なら userName を、そうでなければ "ゲスト" を代入

    これは if 文を使った代入よりも短く書けますが、真偽値への型強制 (Truthy/Falsy) の理解が必要です。

短絡評価はコードを簡潔にする強力な機能ですが、副作用のある式(関数呼び出しなど)を右辺に置く場合は、その式が実行されない可能性があることを理解しておく必要があります。

3.6. 括弧 () を使った評価順序の制御

論理演算子の優先順位は決まっていますが、複雑な条件式では括弧 () を使って評価の順序を明示的に指定することを強く推奨します。これにより、コードの意図が明確になり、予期せぬバグを防ぐことができます。

例:

  • A && B || C(A && B) || C と評価されます。
  • A && (B || C) と書きたい場合は、必ず括弧を使用する必要があります。

先ほどの「ユーザーがログインしており、かつ(管理者であるか編集者であるか)」の例のように、isLoggedIn && (isAdmin || isEditor) と括弧を使うことで、「まず isAdminisEditor のどちらかが真かどうかを判断し、その結果と isLoggedIn が両方真であるか」という意図が明確になります。

括弧を適切に使用することは、複雑な条件式を安全に、かつ意図通りに記述するために不可欠です。迷ったら括弧を使う、というくらいの心がけが良いでしょう。

3.7. Truthy/Falsy な値

JavaScriptでは、論理演算子は真偽値 (true/false) だけでなく、他のデータ型(数値、文字列、オブジェクトなど)に対しても使用できます。これらの非真偽値は、論理演算子の文脈では真偽値として扱われます。これを真偽値への型強制 (Type Coercion to Boolean) と呼びます。

  • Falsy (偽とみなされる値):

    • false
    • 0 (数値のゼロ)
    • -0 (負のゼロ)
    • "" (空文字列)
    • null
    • undefined
    • NaN (Not-a-Number)
  • Truthy (真とみなされる値):

    • Falsyな値以外の全ての値

例えば:

“`javascript
let count = 0;
let userName = “”;
let data = { value: 10 };
let config = null;

if (count && data) { // 0はFalsy, { value: 10 } は Truthy -> false && true -> false
console.log(“これは表示されない”);
}

if (userName || config) { // “”はFalsy, nullはFalsy -> false || false -> false
console.log(“これも表示されない”);
}

if (data && !config) { // { value: 10 }はTruthy, nullはFalsy, !config は !false -> true -> true && true -> true
console.log(“これは表示される”);
}

if (count === 0 && userName === “”) { // 明示的な比較を使うと意図が明確
console.log(“countは0で、userNameは空です。”); // true && true -> true
}
“`

Truthy/Falsyの特性を理解すると、より簡潔な条件式を書ける場合があります。しかし、意図を明確にするためには、特に数値のゼロや空文字列などのFalsy値をチェックする場合は、=== 0=== "" のように明示的な比較演算子を使用することを検討してください。Falsy判定 if (!value) は、valuefalse, 0, "", null, undefined, NaN のいずれかである場合に真となります。これは便利ですが、これらの値を区別したい場合は適していません。

4. if-else if-else 構造での複数条件

セクション2で触れた if-else if-else 構造は、複数の異なる条件(それぞれが単一または複数の論理演算子を含む可能性がある)に基づいて処理を分岐させる最も一般的な方法です。

基本的な構造は再掲しますが、各条件式が複数条件を含む例を見てみましょう。

“`javascript
let temperature = 25;
let isSunny = true;
let isWindy = false;

if (temperature >= 30 && isSunny) {
console.log(“今日は非常に暑い日です。”);
} else if (temperature >= 20 && isSunny && !isWindy) {
console.log(“今日は快適な晴れの日です。”);
} else if (temperature >= 10 && !isSunny) {
console.log(“今日は少し肌寒い曇りの日です。”);
} else {
console.log(“その他の天候です。”);
}
“`

この例では、気象条件に応じて異なるメッセージを表示しています。

  • 最初の if は、「気温が30度以上 かつ 晴れ」の場合。
  • 次の else if は、「気温が20度以上 かつ 晴れ かつ 風がない」の場合。
  • その次の else if は、「気温が10度以上 かつ 晴れではない」の場合。
  • どの条件も満たされない場合は、最後の else が実行されます。

条件の記述順序の重要性:

if-else if-else 構造において、条件式の記述順序は非常に重要です。前述のように、条件は上から順に評価され、最初に真となったブロックが実行されると、それ以降の条件は評価されません。

上記の例で、もし最初の条件 temperature >= 20 && isSunny && !isWindytemperature >= 30 && isSunny の前に書いてしまったらどうなるでしょう?

“`javascript
let temperature = 35; // 30度より高い
let isSunny = true;
let isWindy = false;

// 順番を入れ替えた場合
if (temperature >= 20 && isSunny && !isWindy) {
console.log(“今日は快適な晴れの日です。”); // temperature=35, isSunny=true, !isWindy=true なので、この条件が真となり実行されてしまう
} else if (temperature >= 30 && isSunny) {
console.log(“今日は非常に暑い日です。”); // この条件は評価されない
} else if (temperature >= 10 && !isSunny) {
console.log(“今日は少し肌寒い曇りの日です。”);
} else {
console.log(“その他の天候です。”);
}
“`

この場合、気温が35度でも「快適な晴れの日です」と表示されてしまい、意図した動作になりません。より具体的な(より厳しい)条件から先に記述することで、意図通りの分岐を実現できます。この例では、「30度以上」というより厳しい条件を「20度以上」という緩い条件よりも先に書く必要があります。

if-else if-else 構造は、複数の排他的な条件に基づいて処理を分岐させる場合に非常に有効です。各 else if ブロックは、その前の if および else if の条件がすべて偽であった場合にのみ評価されるという特性を理解することが重要です。

5. 入れ子になった if 文 (Nested if statements)

複数の条件を表現するもう一つの方法として、if 文の中にさらに別の if 文を記述する、入れ子 (Nested) にする方法があります。

例えば、「ユーザーがログインしており、かつ、もし管理者であれば、特別メニューを表示する」という条件を考えてみましょう。これは入れ子を使って表現できます。

“`javascript
let isLoggedIn = true;
let isAdmin = true;

if (isLoggedIn) {
console.log(“ログインしています。”);
if (isAdmin) {
console.log(“あなたは管理者です。特別メニューを表示します。”);
} else {
console.log(“あなたは管理者ではありません。”);
}
} else {
console.log(“ログインしていません。”);
}
“`

この例では、外側の if 文で isLoggedIn をチェックし、それが真の場合にのみ内側の if 文が評価されます。内側の if 文では isAdmin をチェックし、その結果に応じて異なるメッセージが表示されます。

入れ子になった if 文のメリット:

  • 複雑な条件の表現: 論理演算子を多数使用して一つの長い条件式にするよりも、段階的に条件をチェックすることで、思考プロセスに近い形で条件を記述できる場合があります。
  • 関連性の明確化: 外側の条件が満たされた場合にのみ意味を持つ内側の条件がある場合、入れ子構造にすることでその関連性が明確になります。上記の例では、「管理者である」という条件は「ログインしている」という条件が満たされないと意味がありません。

入れ子になった if 文のデメリット:

  • 可読性の低下: ネストが深くなりすぎると、コードが読みにくく、理解しにくくなります。特に複数のレベルでネストされると、どの else がどの if に対応しているのかが分かりづらくなります。これは「矢印コード (Arrow Code)」と呼ばれる問題です。
  • 保守性の低下: 条件の変更や追加が必要になった場合に、ネストされた構造を修正するのが難しくなることがあります。
  • バグの混入リスク: 複雑なネスト構造は、論理的なエラーを引き起こしやすくなります。

いつ入れ子を使うべきか、避けるべきか:

ネストは1〜2レベル程度であれば問題ないことが多いですが、それ以上深くなる場合は避けるべきです。多くの場合、入れ子になった if 文は論理演算子を使った単一の複雑な条件式や、後述する「早期リターン」などのテクニックで書き直すことができます。

上記の例 if (isLoggedIn) { if (isAdmin) { ... } } は、論理AND演算子を使って if (isLoggedIn && isAdmin) { ... } と書く方がより簡潔で可読性が高いでしょう。

入れ子は、外側の条件が満たされた場合にのみ、内側のコードブロック全体が意味を持つ場合(例:オブジェクトがnullでない場合に、そのプロパティにアクセスする、など)に検討する価値がありますが、単純なAND条件であれば論理演算子を使うのが一般的です。

6. 複数の条件を表現する他の方法

JavaScriptでは、if 文以外にも複数の条件に基づいて処理を分岐させるための構文やテクニックが存在します。これらの方法を理解することで、状況に応じて最も適切で読みやすいコードを選択できるようになります。

6.1. switch

switch 文は、一つの式の値と、複数の候補値(case ラベル)を比較して処理を分岐させます。主に等価比較に基づいた複数分岐に適しています。

“`javascript
let dayOfWeek = “水曜日”;

switch (dayOfWeek) {
case “月曜日”:
case “火曜日”:
case “水曜日”:
case “木曜日”:
case “金曜日”:
console.log(“今日は平日です。”);
break; // このbreakがないと次のcaseに処理が続く (fall-through)
case “土曜日”:
case “日曜日”:
console.log(“今日は週末です。”);
break;
default: // どのcaseにも一致しない場合
console.log(“無効な入力です。”);
}
“`

switchif-else if-else の比較:

  • 得意なこと: switch は単一の値と複数の候補値との等価比較(=== に相当)に優れています。特定のプロパティの値や関数の戻り値に基づいて処理を変える場合などに適しています。
  • 苦手なこと: switch は範囲比較(<, > など)や、複数の変数を含む複雑な論理条件 (&&, || を組み合わせたもの) には直接対応できません。そのような場合は if-else if-else が適しています。
  • 可読性: 多数の else if が連続する場合、switch 文の方がコードが整理されて読みやすくなることがあります。ただし、各 case ごとに break を忘れると意図しない動作(fall-through)を引き起こす可能性があるため注意が必要です。
  • パフォーマンス: 多くの case がある場合、JavaScriptエンジンは switch 文を if-else if チェーンよりも効率的に最適化できる場合があります。しかし、これはJavaScriptエンジンの実装に依存し、ほとんどの日常的なケースではパフォーマンスの差は無視できるレベルです。

switch 文で複数条件を「表現」する:

switch 文自体は等価比較ですが、工夫次第で複数条件のように見せることも可能です。例えば、複数の真偽値を組み合わせて文字列を作り、それを switch の式とする方法です。

“`javascript
let isUser = true;
let isAdmin = false;
let isPremium = true;

// 真偽値を組み合わせて状態文字列を作成
let userStatus = ${isUser ? 'User' : 'Guest'}-${isAdmin ? 'Admin' : 'NotAdmin'}-${isPremium ? 'Premium' : 'Free'};
// userStatus は “User-NotAdmin-Premium” のような文字列になる

switch (userStatus) {
case “User-Admin-Premium”:
console.log(“最高の権限を持つプレミアム管理者ユーザーです。”);
break;
case “User-Admin-Free”:
console.log(“管理者権限を持つ無料ユーザーです。”);
break;
case “User-NotAdmin-Premium”:
console.log(“管理者ではないプレミアムユーザーです。”);
break;
// 他のケース…
default:
console.log(“その他のユーザー状態です。”);
}
``
このテクニックは、考えられる状態の組み合わせが比較的少なく、それぞれの状態に対して明確に異なる処理が決まっている場合に有効かもしれません。しかし、状態の組み合わせが増えると
caseが爆発的に増え、可読性や保守性が著しく低下します。多くの場合、このような複雑な状態管理にはif-else if-else` や他の設計パターンの方が適しています。

もう一つの方法は、switch (true) を使う方法です。これは if-else if の代替として使用できます。

“`javascript
let age = 25;

switch (true) { // 式をtrueにする
case age >= 65: // caseの値が true と一致するか評価される
console.log(“高齢者割引が適用されます。”);
break;
case age >= 18:
console.log(“成人です。”);
break;
case age < 18 && age >= 0: // caseの中で論理演算子を使った条件式を書く
console.log(“未成年です。”);
break;
default:
console.log(“無効な年齢です。”);
}
``
この形式は
if-else if構造と非常に似ています。各caseの式がswitchの式trueと厳密に一致するかどうか (===) が評価されます。つまり、case age >= 65:(age >= 65) === trueのように評価されます。この書き方が好みの場合もありますが、一般的には同じロジックをif-else if` で書く方が直感的だと感じる人も多いです。

6.2. 三項演算子 (? :)

三項演算子は、条件に基づいて2つの値のうちのどちらかを選択する、簡潔な構文です。主に簡単な条件に基づく値の選択や代入に使用されます。

javascript
let age = 22;
let status = (age >= 20) ? "成人" : "未成年"; // 条件 ? 真の場合の値 : 偽の場合の値
console.log(status); // 出力: 成人

三項演算子の条件部分には、複数の論理演算子を含む条件式を記述することも可能です。

“`javascript
let isLoggedIn = true;
let hasPermission = false;

let message = (isLoggedIn && hasPermission) ? “アクセス許可” : “アクセス拒否”;
console.log(message); // 出力: アクセス拒否
“`

さらに、三項演算子を連結(ネスト)させることで、複数の条件分岐を表現することもできますが、可読性が低下しやすいため推奨されません。

javascript
// 非推奨な例:三項演算子のネスト
let score = 75;
let grade = (score >= 90) ? "A" :
(score >= 80) ? "B" :
(score >= 70) ? "C" :
"D";
console.log(grade); // 出力: C

この例は、セクション2で示した if-else if-else の例と同じロジックですが、三項演算子を使うと非常に読みにくくなります。三項演算子は、単純な真偽値に基づいて値を選びたい場合に最適であり、複数の条件に基づいて異なる処理ブロックを実行したい場合は if-else if-else を使うのが一般的です。

6.3. 論理演算子だけを使った条件付き処理

セクション3.5で触れた短絡評価の特性を利用して、if 文を使わずに条件付きで処理を実行したり、値を代入したりすることができます。

  • 論理AND (&&) を使った条件付き実行:

    javascript
    let isLoggedIn = true;
    isLoggedIn && console.log("ユーザーはログインしています。"); // isLoggedInがtrueならconsole.logが実行される

    これは if (isLoggedIn) { console.log("ユーザーはログインしています。"); } とほぼ同等です(厳密には戻り値などが異なりますが、副作用として関数を実行するという点では)。ただし、&& の右辺には式(Expression)しか書けません。文(Statement)は書けません。例えば、変数宣言 (let x = 1;) や if 文そのものを && の右辺に直接書くことはできません。関数呼び出しや代入など、評価して値を持つ式を書く必要があります。

  • 論理OR (||) を使ったデフォルト値の設定:

    “`javascript
    let username = null;
    let defaultUsername = username || “ゲスト”; // usernameがFalsyなら”ゲスト”を代入
    console.log(defaultUsername); // 出力: ゲスト

    username = “Alice”;
    defaultUsername = username || “ゲスト”;
    console.log(defaultUsername); // 出力: Alice
    ``
    これは、変数が
    null,undefined,“”,0` などのFalsy値だった場合に、デフォルト値を設定する際によく使われるイディオムです。

これらのテクニックはコードを簡潔にすることができますが、読み慣れていない人には分かりにくい場合もあります。単純なケースに留めておくか、チームのコーディング規約に合わせて使用を判断しましょう。

6.4. ルックアップテーブル / オブジェクトを使った条件分岐

複数の特定の値に基づいて異なる処理を行いたい場合、if-else ifswitch の代わりに、オブジェクト(連想配列)や Map を使って「ルックアップテーブル」を作成する方法があります。

“`javascript
function processStatusCode(statusCode) {
const handlers = {
200: () => console.log(“成功:OK”),
400: () => console.log(“エラー:不正なリクエスト”),
404: () => console.log(“エラー:見つかりません”),
500: () => console.log(“エラー:サーバー内部エラー”)
};

const handler = handlers[statusCode]; // ステータスコードに対応する関数を取得

if (handler) {
handler(); // 該当する関数があれば実行
} else {
console.log(“不明なステータスコードです。”);
}
}

processStatusCode(200); // 出力: 成功:OK
processStatusCode(404); // 出力: エラー:見つかりません
processStatusCode(999); // 出力: 不明なステータスコードです。
“`

この方法は、特定のに対する分岐処理が多い場合に、コードをデータ駆動型にして整理することができます。

メリット:

  • 新しい条件(この例では新しいステータスコード)を追加するのが容易になります(オブジェクトにプロパティを追加するだけ)。
  • 条件とそれに対応する処理が一箇所にまとまり、可読性が向上する場合があります。
  • if-else ifswitch に比べて、特定のケースを探すための線形探索ではなく、ハッシュテーブルによる高速なルックアップが行われるため、多数のケースがある場合にパフォーマンスが向上する可能性があります。

デメリット:

  • 値の範囲に基づいた条件(例: age >= 20)には直接適用できません。
  • オブジェクトのキーは文字列になるため、キーが数値の場合はアクセス時に自動的に文字列に変換されますが、意図しない挙動に注意が必要です。Map を使用すると数値や他の型のキーもそのまま扱えます。

6.5. 関数を使った条件の抽象化/カプセル化

非常に複雑な条件式を書く場合、その条件式自体が非常に長くなり、if 文全体の可読性を損なうことがあります。このような場合は、条件式の一部または全体を別の関数に抽出し、その関数の戻り値(真偽値)を if 文の条件として使用することで、コードを整理できます。

“`javascript
function userCanEditContent(user, content) {
const isLoggedIn = user && user.isLoggedIn;
const isOwner = content && content.ownerId === user.id;
const isAdmin = user && user.role === ‘admin’;
const isEditor = user && user.role === ‘editor’;

// ログインしている かつ (コンテンツの所有者である または 管理者である または 編集者である)
return isLoggedIn && (isOwner || isAdmin || isEditor);
}

// … どこかで呼び出す …
let currentUser = { id: 1, isLoggedIn: true, role: ‘editor’ };
let article = { id: 101, ownerId: 99, title: “記事タイトル” };

if (userCanEditContent(currentUser, article)) {
console.log(“コンテンツを編集できます。”);
} else {
console.log(“コンテンツを編集できません。”);
}

let anotherUser = { id: 2, isLoggedIn: true, role: ‘user’ };
if (userCanEditContent(anotherUser, article)) {
console.log(“コンテンツを編集できます。”);
} else {
console.log(“コンテンツを編集できません。”); // こちらが実行される
}
“`

この例では、複雑な権限チェックのロジックを userCanEditContent という関数にまとめました。if 文の条件式は userCanEditContent(currentUser, article) という非常にシンプルで意図が分かりやすい形になり、可読性が大幅に向上します。

これは、複雑なロジックを関数としてカプセル化し、抽象化するというソフトウェア設計の基本的な原則に基づいています。単に if 文の条件が長くなった場合だけでなく、同じ条件式がコードの複数箇所で出現する場合にも、関数にまとめることで重複を排除し、保守性を高めることができます。

7. 実践的なテクニックと考慮事項

複数の条件を扱う if 文を書く際に、より良いコードを書くための実践的なテクニックと考慮事項をいくつか紹介します。

7.1. コードの可読性

複雑な条件式は、自分自身や他の開発者にとって理解するのが難しくなりがちです。以下の点に注意して、可読性を高めましょう。

  • 複雑な条件式を分割する: 一つの if 文で非常に長い条件式を書くのではなく、中間結果を変数に代入したり、関数に抽出したりすることで、式を分解して読みやすくします。

    “`javascript
    // 分かりにくい例
    if (user !== null && user.profile !== undefined && user.profile.age > 18 && user.isActive && user.hasPaidSubscription && user.subscriptionEndDate > Date.now()) {
    // …
    }

    // 読みやすい例 (変数を使う)
    const isValidUser = user !== null && user.profile !== undefined;
    const isAdult = isValidUser && user.profile.age > 18;
    const isSubscriber = user.isActive && user.hasPaidSubscription && user.subscriptionEndDate > Date.now();

    if (isValidUser && isAdult && isSubscriber) {
    // …
    }

    // さらに良い例 (関数を使う) – 前述の6.5の例を参照
    // if (canAccessPremiumContent(user)) { … }
    “`

  • 変数や定数を使って意味を明確にする: 真偽値の変数名には、その真偽値が何を表しているのかが明確に分かる名前(例: isLoggedIn, hasAdminAccess, canProcessOrder など)を付けましょう。これにより、条件式を見ただけでその意味を推測しやすくなります。

  • 適切なインデントと改行: 複数の条件を &&|| で繋ぐ場合、適切な位置で改行し、インデントを揃えることで、条件式の構造を視覚的に分かりやすくすることができます。

    javascript
    if (condition1 &&
    condition2 &&
    (condition3 || condition4) && // 括弧内の条件をグループ化
    condition5) {
    // ...
    }

  • 否定形の使用を検討する: !condition のように否定形を使うと、肯定形を使うよりも直感的に分かりにくい場合があります。例えば if (!userExists) よりも if (user === null)if (user === undefined) の方が具体的な状態を示しているため分かりやすいかもしれません。ただし、これは文脈によります。例えば、isAvailable という変数に対して if (!isAvailable) は「利用可能ではない場合」と自然に読めます。

7.2. パフォーマンス

ほとんどのアプリケーションにおいて、条件式の評価そのものがボトルネックになることは稀です。しかし、条件式の中にコストの高い処理(データベースへの問い合わせ、ネットワークリクエスト、複雑な計算など)が含まれる場合は、パフォーマンスを考慮する必要があります。

  • 短絡評価の活用: コストの高い処理を論理演算子の右辺に配置し、左辺で簡単な(高速な)条件チェックを行うことで、不要なコストの高い処理の実行を回避できます。

    “`javascript
    // 最初の条件(quickCheck)がfalseなら、costlyOperation()は実行されない
    if (quickCheck() && costlyOperation()) {
    // …
    }

    // 最初の条件(quickCheck)がtrueなら、costlyOperation()は実行されない
    if (quickCheck() || costlyOperation()) {
    // …
    }
    “`

  • 条件の並び順 (if-else if-else): if-else if-else 構造では、最も可能性の高い条件を最初に配置することで、平均的な評価回数を減らし、わずかにパフォーマンスを向上させることができます。ただし、可読性やロジックの正確性を犠牲にしてまでマイクロ最適化を行う必要はありません。論理的な順序(例: より具体的な条件から先に書く)を優先しつつ、可能性の高いものを考慮に入れる程度が良いでしょう。

7.3. デバッグ

複雑な条件式を含む if 文が期待通りに動作しない場合、デバッグが難しくなることがあります。

  • 条件式の途中結果をログ出力する: 複雑な条件式の場合、各部分式や論理演算子の途中結果がどのように評価されているのかを console.log() で確認すると原因特定に役立ちます。

    “`javascript
    let cond1 = condition1;
    let cond2 = condition2;
    let cond3or4 = condition3 || condition4;
    let finalCondition = cond1 && cond2 && cond3or4 && condition5;

    console.log(condition1: ${cond1});
    console.log(condition2: ${cond2});
    console.log(condition3 || condition4: ${cond3or4});
    console.log(finalCondition: ${finalCondition});

    if (finalCondition) {
    // …
    }
    “`

  • 複雑な条件式を分解してテストする: 一つの長い条件式ではなく、いくつかの単純な条件式に分解し、それぞれが期待通りに評価されるかを確認します。

7.4. Boolean型の重要性

JavaScriptは動的な型付け言語であり、多くの異なる型が真偽値のコンテキストで評価されます (Truthy/Falsy)。これは簡潔なコードを書くのに便利ですが、意図しない挙動を引き起こす原因にもなります。

  • 明示的な比較: nullundefined0"" などのFalsy値を区別したい場合は、== null=== 0 のように明示的な比較演算子を使用することが重要です。

    “`javascript
    let count = 0;
    if (count) { // count が 0 なので Falsy -> false と評価される
    console.log(“カウントはゼロではありません。”); // これは実行されない
    }

    if (count === 0) { // 明示的な比較 -> true と評価される
    console.log(“カウントはゼロです。”); // これは実行される
    }
    “`

  • Boolean() 関数による明示的な型変換: ある値が Truthy か Falsy かを明示的に確認したい場合や、真偽値に変換して変数に代入したい場合は、Boolean() 関数を使用できます。

    javascript
    let value = "";
    let isTruthy = Boolean(value); // isTruthy は false

7.5. ネストの深さ (Arrow Code) と早期リターン

前述の入れ子になった if 文のデメリットとして挙げた「ネストが深くなりすぎる問題(矢印コード)」は、可読性と保守性を著しく低下させます。

javascript
// 典型的な矢印コードの例
if (condition1) {
if (condition2) {
if (condition3) {
// 実際の処理
} else {
// condition3 が false の場合の処理
}
} else {
// condition2 が false の場合の処理
}
} else {
// condition1 が false の場合の処理
}

このような構造は、コードが右方向にインデントされていき、視覚的に「矢印」のように見えることから矢印コードと呼ばれます。

これを改善するための一般的なテクニックとして、早期リターン (Early Return) またはガード節 (Guard Clause) があります。これは、条件が満たされない場合に、関数の先頭で returnthrow を使って早期に処理を終了させる手法です。

“`javascript
function processRequest(request) {
if (!request.isValid) {
console.log(“不正なリクエストです。”);
return; // 不正なリクエストの場合はここで終了
}

if (!request.isAuthenticated) {
console.log(“認証されていません。”);
return; // 認証されていない場合はここで終了
}

if (!userHasPermission(request.user, request.resource)) {
console.log(“権限がありません。”);
return; // 権限がない場合はここで終了
}

// ここまで到達するのは、すべての前提条件が満たされた場合のみ
console.log(“リクエストを処理します。”);
// 実際の主要な処理
}
“`

この例では、処理を進めるための前提条件が満たされない場合に、関数の早い段階で return しています。これにより、主要な処理のロジックは最もネストの浅い位置に記述でき、コードの可読性が大幅に向上します。複数の条件チェックが必要な関数の先頭で、不正な入力や権限不足などをチェックして早期に終了させるパターンは非常に効果的です。

7.6. デモルガンの法則

複雑な条件式、特に否定形 (!) を含む条件式を扱う際に役立つ論理学の法則として、ド・モルガンの法則があります。

  • !(A && B)!A || !B と等価です。
    例: 「暑くなく、晴れでもない」(!(isHot && isSunny)) は、「暑くない または 晴れではない」(!isHot || !isSunny) と等価です。
  • !(A || B)!A && !B と等価です。
    例: 「週末でも祝日でもない」(!(isWeekend || isHoliday)) は、「週末ではなく かつ 祝日でもない」(!isWeekend && !isHoliday) と等価です。

これらの法則を知っていると、否定形を含む複雑な条件式を、より直感的に理解しやすい形に変換できることがあります。

“`javascript
// 例: ユーザーがアクティブでなく、かつ管理者でもない場合
if (!isActiveUser && !isAdmin) {
console.log(“アクティブな管理者以外のユーザーです。”);
}

// デモルガンを使うと…
// !(isActiveUser || isAdmin) とは等価ではありません。
// !isActiveUser && !isAdmin は !(isActiveUser || isAdmin) と等価… ではなく、
// !isActiveUser && !isAdmin は「アクティブではない かつ 管理者ではない」
// !(isActiveUser || isAdmin) は「(アクティブである または 管理者である) ではない
// これは同じ意味です。

// どちらの表現が分かりやすいかは文脈や好みによります。
// if (!(isActiveUser || isAdmin)) { // アクティブか管理者のどちらかである、という条件が満たされない場合
// console.log(“アクティブな管理者以外のユーザーです。”);
// }
“`

複雑な否定条件を扱う際に、一時的にデモルガンの法則を適用して別の表現に変換してみることで、ロジックの誤りに気づいたり、より分かりやすい記述を見つけたりできることがあります。

8. 具体的な使用例

これまでに学んだ知識を活かして、様々なシナリオでの複数条件の具体的な使用例を見ていきましょう。

例1: ユーザー入力の検証

フォームから送信されたデータが、複数の条件を満たしているかチェックする。

“`javascript
function validateUserData(userData) {
const isNameValid = typeof userData.name === ‘string’ && userData.name.trim().length > 0;
const isAgeValid = typeof userData.age === ‘number’ && userData.age >= 0 && userData.age <= 120;
const isEmailValid = typeof userData.email === ‘string’ && userData.email.includes(‘@’); // 簡易的なチェック

if (!isNameValid) {
return “名前は必須です。”;
} else if (!isAgeValid) {
return “有効な年齢を入力してください。”;
} else if (!isEmailValid) {
return “有効なメールアドレスを入力してください。”;
} else {
return “検証成功”;
}
}

let user1 = { name: “Alice”, age: 30, email: “[email protected]” };
let user2 = { name: “”, age: 25, email: “bob@test” };
let user3 = { name: “Charlie”, age: -5, email: “[email protected]” };

console.log(validateUserData(user1)); // 出力: 検証成功
console.log(validateUserData(user2)); // 出力: 名前は必須です。
console.log(validateUserData(user3)); // 出力: 有効な年齢を入力してください。
``
ここでは、各フィールドの検証ロジックを変数に抽出し、
if-else if` 構造でエラーメッセージを優先順位順に返しています。各検証条件は、型のチェックや値の範囲、特定の文字の有無などを組み合わせた複数条件になっています。

例2: 特定の状態に応じた処理の切り替え

注文の状態やユーザーのステータスなどに応じて、実行する処理を変更する。

“`javascript
function processOrderStatus(order) {
const isPending = order.status === ‘pending’;
const isProcessing = order.status === ‘processing’;
const isShipped = order.status === ‘shipped’;
const isDelivered = order.status === ‘delivered’;
const isCancelled = order.status === ‘cancelled’;

if (isPending && order.paymentReceived) {
console.log(“注文を処理中に変更します。”);
// processOrder(order);
} else if (isProcessing && order.itemsPacked) {
console.log(“注文を発送済みに変更します。”);
// shipOrder(order);
} else if (isShipped && order.deliveryConfirmed) {
console.log(“注文を配達済みに変更します。”);
// completeOrder(order);
} else if (isCancelled) {
console.log(“注文はキャンセルされました。”);
// handleCancelledOrder(order);
} else {
console.log(注文状態: ${order.status} に対応する処理はありません。);
}
}

processOrderStatus({ status: ‘pending’, paymentReceived: true }); // 出力: 注文を処理中に変更します。
processOrderStatus({ status: ‘processing’, itemsPacked: true }); // 出力: 注文を発送済みに変更します。
processOrderStatus({ status: ‘shipped’, deliveryConfirmed: false }); // 出力: 注文状態: shipped に対応する処理はありません。
processOrderStatus({ status: ‘cancelled’ }); // 出力: 注文はキャンセルされました。
``
この例では、注文の状態と他の条件(支払い済みか、梱包済みか、配達確認済みかなど)を組み合わせて、次に実行すべきアクションを判断しています。各条件式は
&&` を使った複数条件になっています。

例3: 複雑な割引ルールの適用

商品の価格計算において、複数の条件(顧客タイプ、購入数量、プロモーションコードなど)に基づいて割引率を決定する。

“`javascript
function calculateDiscount(customer, itemPrice, quantity, promoCode) {
let discountRate = 0;
const isPremiumCustomer = customer.type === ‘premium’;
const isBulkPurchase = quantity >= 10;
const hasValidPromo = promoCode === ‘SAVE10’;

if (isPremiumCustomer && isBulkPurchase && hasValidPromo) {
discountRate = 0.20; // プレミアム顧客 AND 大量購入 AND 有効なプロモコードで20%オフ
console.log(“プレミアム顧客 + 大量購入 + プロモコード割引適用”);
} else if (isPremiumCustomer && (isBulkPurchase || hasValidPromo)) {
discountRate = 0.15; // プレミアム顧客 AND (大量購入 OR 有効なプロモコード) で15%オフ
console.log(“プレミアム顧客 + (大量購入 or プロモコード) 割引適用”);
} else if (isBulkPurchase || hasValidPromo) {
discountRate = 0.10; // 大量購入 OR 有効なプロモコードで10%オフ
console.log(“大量購入 or プロモコード割引適用”);
} else if (isPremiumCustomer) {
discountRate = 0.05; // プレミアム顧客で5%オフ
console.log(“プレミアム顧客割引適用”);
} else {
console.log(“割引なし”);
}

const finalPrice = itemPrice * quantity * (1 – discountRate);
return finalPrice;
}

let customer1 = { type: ‘premium’ };
let customer2 = { type: ‘regular’ };

console.log(“— 顧客1 (プレミアム) —“);
console.log(価格: ${calculateDiscount(customer1, 100, 5, 'SAVE10')}); // プレミアム + プロモ -> 15%オフ (10050.85 = 425)
console.log(価格: ${calculateDiscount(customer1, 100, 12, 'SAVE10')}); // プレミアム + 大量 + プロモ -> 20%オフ (100120.8 = 960)
console.log(価格: ${calculateDiscount(customer1, 100, 12, 'INVALID')}); // プレミアム + 大量 -> 15%オフ (100120.85 = 1020)
console.log(価格: ${calculateDiscount(customer1, 100, 5, 'INVALID')}); // プレミアム -> 5%オフ (10050.95 = 475)

console.log(“\n— 顧客2 (一般) —“);
console.log(価格: ${calculateDiscount(customer2, 100, 5, 'SAVE10')}); // プロモ -> 10%オフ (10050.9 = 450)
console.log(価格: ${calculateDiscount(customer2, 100, 12, 'SAVE10')}); // 大量 + プロモ -> 10%オフ (100120.9 = 1080)
console.log(価格: ${calculateDiscount(customer2, 100, 12, 'INVALID')}); // 大量 -> 10%オフ (100120.9 = 1080)
console.log(価格: ${calculateDiscount(customer2, 100, 5, 'INVALID')}); // 割引なし (10051 = 500)
``
この例では、最も優先順位の高い割引条件(複数の条件が組み合わさったもの)から順に
if-else ifでチェックしています。条件式の中では&&||`、そして括弧を組み合わせて複雑な論理を表現しています。

これらの例から分かるように、現実世界の多様な要件を満たすためには、単一の条件だけでなく、論理演算子を駆使して複数の条件を適切に組み合わせることが不可欠です。if-else if-else 構造、入れ子、そして他の代替手段を理解し、状況に応じて最適な方法を選択することが、効率的で保守性の高いコードを書く鍵となります。

9. まとめ

この記事では、JavaScriptの if 文で複数の条件を扱う方法について、基本から応用、実践的なテクニックまでを詳細に解説しました。

  • if 文は条件に基づいてプログラムの実行フローを制御する基本的な構文です。
  • 複数の条件を組み合わせるためには、論理演算子 (&&||!) を使用します。
  • && (AND) は両方が真の場合に真、|| (OR) は少なくとも一方が真の場合に真、! (NOT) は真偽値を反転させます。
  • 複数の論理演算子を組み合わせる際は、演算子の優先順位と、括弧 () による明示的な順序指定が重要です。
  • 短絡評価 (Short-Circuit Evaluation) は、論理演算子の評価を効率化し、コードを簡潔にする強力な機能です。
  • if-else if-else 構造は、複数の排他的な条件に基づいて処理を分岐させるための一般的な方法であり、条件の記述順序が重要です。
  • 入れ子になった if 文は複雑な条件を段階的に表現できますが、ネストが深くなると可読性や保守性が低下します。
  • switch 文、三項演算子、論理演算子による条件付き処理、ルックアップテーブルなど、if 文以外にも複数条件を扱うための選択肢があります。状況に応じて最も適した方法を選ぶことが重要です。
  • 可読性、パフォーマンス、デバッグ、Truthy/Falsyな値の理解、早期リターンなどの実践的な考慮事項は、堅牢で分かりやすいコードを書くために不可欠です。

JavaScriptにおける複数条件の扱いは、日々のプログラミングで最も頻繁に遭遇するタスクの一つです。この記事で解説した内容を理解し、様々なパターンやテクニックを使いこなせるようになることで、より洗練された、メンテナンスしやすいJavaScriptコードを書くことができるようになるでしょう。複雑な条件に直面した際は、単に動くコードを書くだけでなく、「どのように書けば最も意図が明確で、将来の変更にも強いか」を意識して、最適な表現方法を選択する習慣をつけましょう。

コメントする

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

上部へスクロール