JavaScriptによるアプリケーション開発において、条件分岐はプログラムの根幹を成す重要な要素です。
しかし、要件が複雑になるにつれてif文の条件式が肥大化し、可読性や保守性が著しく低下する</cst-コード>という課題に直面することが多々あります。
複数の条件を組み合わせる際、単に論理演算子を並べるだけでは、後からコードを読み返したときに意図が伝わりにくく、バグの温床にもなりかねません。
本記事では、JavaScriptでif文の複数条件をスマートに記述するためのテクニックから、設計レベルでコードをクリーンに保つ手法までを詳しく解説します。
論理演算子の基礎と短絡評価の活用
JavaScriptの条件分岐で最も基本となるのが、論理積&&(AND)と論理和||(OR)です。
これらを適切に組み合わせることは基本ですが、その動作原理である短絡評価(ショートサーキット)を理解しておくことで、より効率的なコードが書けるようになります。
短絡評価(Short-circuit evaluation)の仕組み
短絡評価とは、左から右へ条件を評価していく際、結果が確定した時点で残りの評価をスキップする仕組みです。
&&演算子:左辺がfalseの場合、右辺は評価されません。||演算子:左辺がtrueの場合、右辺は評価されません。
これを利用することで、nullチェックとプロパティアクセスを1行で行うなどの記述が可能になります。
// 短絡評価を活用した安全なアクセス
const user = { profile: { name: "田中" } };
if (user && user.profile && user.profile.name) {
console.log("ユーザー名が存在します:", user.profile.name);
}
ユーザー名が存在します: 田中
ただし、現在では後述するオプショナルチェイニングを使用する方がよりスマートです。
複数条件をスマートにするリファクタリング手法
条件式が3つ、4つと増えていく場合、if文の中にすべてを詰め込むのは避けましょう。
ここでは、可読性を劇的に向上させる具体的なテクニックを紹介します。
1. 配列の includes メソッドを活用する
特定の変数が複数の値のいずれかに合致するかを確認したい場合、||(OR)で繋げるのではなく、
改善前のコード(冗長なOR演算)
const status = "pending";
if (status === "active" || status === "pending" || status === "processing") {
console.log("処理を続行します");
}
改善後のコード
const status = "pending";
const validStatuses = ["active", "pending", "processing"];
if (validStatuses.includes(status)) {
console.log("処理を続行します");
}
処理を続行します
この手法の利点は、条件となる値がリスト化されているため、追加や削除が容易である点にあります。
条件が増えてもif文自体を修正する必要はありません。
2. Array.every や Array.some を利用する
動的に変化する複数の条件をすべて満たしているか、あるいはいずれかを満たしているかを判定する場合、Array.every や Array.some が非常に有効です。
const criteria = [
{ id: 1, met: true },
{ id: 2, met: true },
{ id: 3, met: false }
];
// すべての条件を満たしているか
const allMet = criteria.every(c => c.met);
// いずれかの条件を満たしているか
const anyMet = criteria.some(c => c.met);
if (anyMet) {
console.log("少なくとも1つの条件を満たしています");
}
少なくとも1つの条件を満たしています
3. ガード句(Early Return)でネストを解消する
複数の条件を「すべて満たす場合のみ実行する」というロジックを書く際、if文をネストさせてしまうと、コードの可読性が著しく低下します。
これを防ぐのがガード句という考え方です。
改善前のネストが深いコード
function processOrder(order) {
if (order) {
if (order.items.length > 0) {
if (order.status === "paid") {
console.log("発送準備を開始します");
}
}
}
}
改善後のコード(ガード句を使用)
function processOrder(order) {
// 異常系や除外条件を先にチェックしてreturnする
if (!order) return;
if (order.items.length === 0) return;
if (order.status !== "paid") return;
// 本筋の処理
console.log("発送準備を開始します");
}
ガード句を採用することで、「どのような条件のときに処理を行わないか」が明確になり、メインロジックのインデントを浅く保つことができます。
オプショナルチェイニングとNull合体演算子
2020年以降のJavaScript標準仕様(ES2020)で導入され、現在の開発では欠かせない存在となったのがオプショナルチェイニング(?.)とNull合体演算子(??)です。
これらは、複数条件の中でも特に「値の存在確認」を劇的に簡略化します。
オプショナルチェイニングの威力
深い階層にあるプロパティを参照する場合、以前は各階層での存在チェックが必要でした。
// 以前の書き方
if (user && user.config && user.config.theme === "dark") {
// 処理
}
// 現代的な書き方
if (user?.config?.theme === "dark") {
// 処理
}
?. は、その前の値が null または undefined であれば評価を止めて undefined を返します。
これにより、エラーを回避しつつ簡潔な条件式が記述可能です。
Null合体演算子との組み合わせ
デフォルト値を設定しつつ条件分岐させたい場合は、?? が役立ちます。
const userSettings = {
retryCount: 0
};
// OR演算子だと 0 が falsy なため 3 が代入されてしまう
const countOld = userSettings.retryCount || 3;
// Null合体演算子なら null / undefined の場合のみ 3 を採用する
const countNew = userSettings.retryCount ?? 3;
console.log("Old:", countOld);
console.log("New:", countNew);
Old: 3
New: 0
複雑な条件を「意味のある変数」に抽出する
if文の括弧内に複数の論理演算子が並ぶと、何を判定しようとしているのか直感的に理解できなくなります。
このような場合、条件式を記述的な変数名に代入することで、コードが自己記述的になります。
条件の変数抽出
可読性が低い例
if (user.age >= 18 && user.hasLicense && user.isSober) {
console.log("運転を許可します");
}
可読性が高い例
const isAdult = user.age >= 18;
const canDrive = user.hasLicense && user.isSober;
if (isAdult && canDrive) {
console.log("運転を許可します");
}
このように、条件を分割して変数に格納することで、「なぜその判定を行っているのか」という意図を明確に伝えることができます。
また、デバッグ時に変数の値を確認しやすくなるという副次的なメリットもあります。
条件分岐をオブジェクトやMapで代替する
if文やelse if文が延々と続く場合、それは条件分岐ではなくマッピング(対応付け)の問題かもしれません。
ロジックをデータとして分離することで、コードをスマートに保てます。
オブジェクトリテラルによるルックアップ
const getDiscountRate = (membershipType) => {
// if文の羅列の代わりにオブジェクトを使う
const rates = {
"regular": 0,
"silver": 0.05,
"gold": 0.1,
"platinum": 0.2
};
return rates[membershipType] ?? 0;
};
console.log("割引率:", getDiscountRate("gold"));
割引率: 0.1
この手法は「特定の値に基づいて結果を返す」という処理において非常に強力です。
条件が増えても、関数内のロジックを書き換えることなく、rates オブジェクトに新しいキーを追加するだけで対応可能です。
高度なパターン:関数の抽出と組み合わせ
条件がさらに複雑で、動的な計算や非同期処理が絡む場合は、条件判定自体を小さな関数に切り出すことが推奨されます。
const isEligibleForPromotion = (user) => {
if (!user.isActive) return false;
if (user.points < 1000) return false;
const hasPremiumStatus = user.rank === "gold" || user.rank === "platinum";
const isLoyalCustomer = user.yearsActive > 5;
return hasPremiumStatus || isLoyalCustomer;
};
if (isEligibleForPromotion(currentUser)) {
// プロモーション処理
}
このように、条件を関数にカプセル化することで、if文の記述は if (isEligibleForPromotion(user)) という極めてシンプルな形になります。
これにより、上位のロジックがビジネスルールに集中できるようになります。
条件の優先順位と論理的な簡素化
複数条件を扱う際、論理的な整理を行うことで、条件式自体を短くできる場合があります。
ド・モルガンの法則の活用
否定条件が重なる複雑な式は、ド・モルガンの法則を適用するとスッキリすることがあります。
!(A && B)は!A || !Bと等価!(A || B)は!A && !Bと等価
人間の脳は「否定の論理和」よりも「肯定の論理積」を理解しやすいため、可能な限り肯定的な表現に書き換えることが可読性向上の鍵となります。
評価コストを意識した配置
複数の条件を && で繋ぐ際、「最も判定コストが低く、かつ失敗する可能性が高い条件」を左側に配置しましょう。
これにより、短絡評価の恩恵を最大限に受け、不要な計算を避けることができます。
まとめ
JavaScriptにおいてif文の複数条件をスマートに記述するためには、単なる構文の知識だけでなく、「いかに読み手の認知負荷を下げるか」という視点が欠かせません。
本記事で紹介した手法を振り返ります。
| 手法 | 効果 |
|---|---|
| includesメソッド | 複数の値のいずれかに合致するかを簡潔に判定する |
| ガード句 | ネストを排除し、メインロジックを際立たせる |
| オプショナルチェイニング | undefined/nullによるエラーを防ぎつつ、存在チェックを短縮する |
| 変数の抽出 | 複雑な論理式に名前を付け、意図を明確にする |
| オブジェクトルックアップ | else ifの羅列をデータ構造として整理する |
これらのテクニックを状況に応じて適切に使い分けることで、JavaScriptのコードはより洗練され、変更に強いものになります。
まずは、今書いているif文の中に、変数に切り出せる条件や、配列のメソッドで代用できる箇所がないか探してみてください。
小さな改善の積み重ねが、プロジェクト全体の品質向上に繋がります。
