やあ、今日もコードと向き合ってくれてお疲れ様。
フロントエンドの現場にいると、APIから返ってきたデータのデフォルト値を設定したり、UIの表示フラグを制御したりする場面に毎日のように遭遇するよね。
そこでよくあるのが、「あれ、この値が `0` や `””`(空文字)のときに、予期せぬフォールバックが発動してレイアウトが崩れたんだけど……」 というバグだ。大抵、犯人は `||`(論理和)の雑な使い方。
今回は、JavaScriptの中級者なら絶対に押さえておくべき、論理和(`||`)とNull合体演算子(`??`)の決定的な違いについて、ブラウザの裏側の動きや実務でのユースケースを交えながら徹底的に解説しよう。ここをクリアにすると、バグの温床が一つ綺麗に消えるぞ。
—
1. そもそも何が違うのか?判定基準の正体
結論から言おう。
- `||` は 「Falsy値」 すべてをキャッチして右側の値にフォールバックする。
- `??` は 「Nullish値(`null` または `undefined`)」 だけをキャッチして右側の値にフォールバックする。
この「Falsy」と「Nullish」の境界線を正確に理解しているかどうかが、プロとアマの分かれ道だ。
JavaScriptにおけるFalsyな値たち
JavaScriptの世界では、以下の6つの値が `if文` などの条件分岐で「偽(falsy)」として判定される。
1. `false`
2. `0`(数値のゼロ)
3. `-0`(マイナスゼロ)
4. `0n`(BigIntのゼロ)
5. `””`, `”`, “ “(空文字)
6. `null`
7. `undefined`
8. `NaN`
論理和(`||`)は、左側のオペランドがこの「Falsyな値」のどれかであれば、問答無用で右側の値を採用してしまう。これが実務で厄介なバグを生む原因になるんだ。
—
2. 実務でよくある「事故」の現場
例えば、ユーザーの残高(`balance`)や、フォームの入力文字数(`inputLength`)を表示するコンポーネントを考えてみよう。
// ユーザーの残高が 0円 のケース
const userBalance = 0;
// 悪名高い || (論理和)を使ったフォールバック
const displayBalance = userBalance || 100;
console.log(displayBalance); // 100 になってしまう!(本当は 0 を表示したいのに!)
どうだい? ユーザーの口座残高が奇跡的に「0円」なのに、画面には「100円」と表示されてしまった。ユーザーからクレームが来るレベルの致命的なバグだよね。なぜこうなるかというと、JavaScriptのエンジンは `0` を「値が存在しない(falsy)」と勝手に解釈して、右側のデフォルト値 `100` を選んでしまったからだ。
ここで登場するのが、ES2020で導入された Null合体演算子(`??`) だ。
const userBalance = 0;
// Null合体演算子を使った正しいフォールバック
const displayBalance = userBalance ?? 100;
console.log(displayBalance); // 0 が正しく表示される!
`??` は、左側の値が `null` または `undefined` のときだけ 右側にフォールバックする。したがって、`0` も `””`(空文字)も、有効なデータ(Valid Value)としてそのまま通してくれるんだ。
—
3. ブラウザの裏側:どうやって判定しているのか?
少しだけエンジニアとしての裏側の話をしておこう。
JavaScriptエンジン(V8など)は、式を評価するときに内部で型変換や値の評価を行っている。
論理和 `A || B` は、実質的に次のような評価をしている。
1. `A` を真偽値(Boolean)に強制変換(ToBoolean)する。
2. その結果が `true` なら、変換前の `A` を返す。
3. `result` が `false` なら、`B` を返す。
この「ToBoolean」の処理において、`0` や `””` は容赦なく `false` に落とされてしまう。これが原因だ。
一方、Null合体演算子 `A ?? B` は、ECMAScript仕様上では 「Nullish coalescing operator (??)」 として定義されており、ToBoolean ではなく、厳密な同値演算子(`===`)に近い感覚で、値が `null` または `undefined` かどうかだけをピンポイントでチェックしている。
無駄な型変換が走らない分、意図しないバグを防ぎやすいし、コードの意図が明確になるというわけだ。
—
4. 現場ですぐに使える!ベストプラクティス集
それじゃあ、実務のコードベースでどう使い分けるべきか、具体的なパターンを見ていこう。
パターンA: ユーザーの入力・設定値・数値を扱う場合
数値の `0` や、位置情報(緯度・経度で `0` や負の値があり得る)を扱うときは、必ず `??` を使う。
// 【NG】文字数が 0 のときにデフォルト値にすり替わってしまう
const text = inputEl.value.length || 10;
// 【OK】空文字や0文字は「有効な入力」として扱う
const text = inputEl.value.length ?? 10;
パターンB: UIのテキストやラベルのフォールバック
「もしAPIから名前が返ってきたらそれを表示、なければ “ゲスト”」という要件の場合、APIが空文字 `””` を返す可能性を考慮しよう。
// APIからのレスポンス
const response = { name: “” };
// || だと、空文字 “” も Falsy なので “ゲスト” になってしまう
const displayName1 = response.name || “ゲスト”; // “ゲスト”
// データベースの設計上、未登録は null や undefined で返ってくるなら ?? が安全
const displayName2 = response.name ?? “ゲスト”; // “” (空文字が維持される)
// もし「空文字すら未登録とみなしたい(””ならゲストにしたい)」というビジネス要件なら、
// 意図的に || を使う、あるいは三項演算子で明示的に書くべき。
const displayName3 = response.name ? response.name : “ゲスト”;
プロの現場のコツ: 「空文字を許容するかどうか」はビジネスロジックに直結する。単なる「便利だから」という理由だけで `??` や `||` を選ぶな。「この変数は `null` / `undefined` になり得るのか?」を常に意識しよう。
パターンC: オプションや設定オブジェクトのデフォルト値結合
関数に渡す引数のオプションオブジェクトを展開するときなども `??` が大活躍する。
function createUser(options = {}) {
// 設定が渡されなかったプロパティだけを安全にフォールバック
const timeout = options.timeout ?? 3000;
const retries = options.retries ?? 3;
const debugMode = options.debugMode ?? false; // false は Falsy だが、?? なら上書きされない!
console.log({ timeout, retries, debugMode });
}
createUser({ debugMode: false });
// 結果: { timeout: 3000, retries: 3, debugMode: false }
// もしここで || を使っていたら、debugMode が強制的に true に化けていたはずだ。
—
⚠️ 1つだけ注意! `&&` や `||` との混合禁止の罠
JavaScriptの仕様として、`??` を `&&` や `||` と直接組み合わせることは、括弧(`()`)で優先順位を明示しないと構文エラー(SyntaxError)になるように厳格に決まっている。
// これはエラーになる!
const x = a && b ?? c;
// 正しく優先順位を括弧で囲む必要がある
const x = (a && b) ?? c;
エンジンが「どっちの演算子を先に評価していいか分からない」と混乱するのを防ぐための仕様だ。複雑な条件式を書くときは、面倒くさがらずに必ず括弧を使おう。コードの可読性も劇的に上がる。
—
まとめ
- `||`(論理和) は、Falsyな値(`0`, `””`, `false`, `null`, `undefined` など)すべてをキャッチして右側に倒す。
- `??`(Null合体演算子) は、`null` と `undefined` のみをキャッチする。数値の `0` や空文字を正しく維持したいときはこっち。
- 実務では「値が存在しない状態」が具体的に何を指すのか(`null` なのか、`0` も含めるのか)を仕様書やAPI仕様から読み解き、適切な演算子を選ぶこと。
「動けばいいや」で `||` を乱用していると、思わぬエッジケースでユーザーのデータを書き換えてしまったり、表示バグを踏んだりする。今日のこの記事を機に、手元のコードの `||` が本当に正しいかどうか、見直してみてほしい。
それじゃあ、今日も良いコードを書こう!

コメント