JavaScriptで開発を行っている際、変数に値が入っていない場合の「デフォルト値」を設定するシーンは非常に多く存在します。

かつては論理和演算子である||を使用するのが一般的でしたが、意図しない挙動に悩まされる開発者も少なくありませんでした。

そこで登場したのがヌル値合体演算子(??)です。

現代のJavaScript開発において、この演算子を正しく理解し、論理和演算子と使い分けることは、バグの少ない堅牢なコードを書くための必須スキルと言えます。

本記事では、ヌル値合体演算子の基本的な仕組みから、現場で役立つ実践的な活用テクニック、そして論理和演算子との決定的な違いについて詳しく解説します。

ヌル値合体演算子(??)とは何か

ヌル値合体演算子??は、左辺の値がnullまたはundefinedである場合のみに右辺の値を返し、それ以外の場合には左辺の値を返す論理演算子です。

この演算子の最大の目的は、「値が存在しない状態」を厳密に定義し、適切なデフォルト値を提供することにあります。

基本的な構文

構文は非常にシンプルです。

JavaScript
// 基本的な書き方
const result = value1 ?? value2;

このコードでは、value1nullまたはundefinedであれば、resultにはvalue2が代入されます。

もしvalue1に何らかの値(数値の0や空文字を含む)が入っていれば、そのままvalue1が代入されます。

なぜ「ヌル値」なのか

JavaScriptには「値がない」ことを示す概念として、nullundefinedの2種類が存在します。

ヌル値合体演算子は、これら2つの値のみを特別扱いします。

これ以外の値、例えば数値の0、空文字列""、ブール値のfalseなどは、すべて「値がある(有効な値)」として扱われます。

論理和演算子(||)との決定的な違い

これまでデフォルト値の設定に使われてきた論理和演算子||と、ヌル値合体演算子??の最大の違いは、「偽値(Falsy)」の扱いにあります。

偽値(Falsy)の復習

JavaScriptにおいて、以下の値は論理評価においてfalseと見なされます。

説明
falseブール値の偽
0数値のゼロ
-0数値のマイナスゼロ
0nBigIntのゼロ
""空文字列
null値の欠如(明示的)
undefined未定義(暗黙的)
NaN数値ではない

論理和演算子||は、左辺が上記のいずれかであれば、必ず右辺を返します。

しかし、プログラムのロジックによっては、0や空文字列""有効な値として扱いたい場合があります。

比較コードによる違いの検証

以下のコードで、それぞれの演算子の挙動を比較してみましょう。

JavaScript
const count = 0;
const text = "";

// 論理和演算子 (||) の場合
const resultOr1 = count || 10;
const resultOr2 = text || "Default Text";

console.log("|| result (count):", resultOr1);
console.log("|| result (text):", resultOr2);

// ヌル値合体演算子 (??) の場合
const resultNullish1 = count ?? 10;
const resultNullish2 = text ?? "Default Text";

console.log("?? result (count):", resultNullish1);
console.log("?? result (text):", resultNullish2);
実行結果
|| result (count): 10
|| result (text): Default Text
?? result (count): 0
?? result (text):

論理和演算子を使用したケースでは、0や空文字列が意図せずデフォルト値に上書きされていることがわかります。

これに対してヌル値合体演算子は、左辺が確実にnullundefinedのときだけ動作するため、データの精度を保つことができます。

実践的な活用テクニック

ヌル値合体演算子は、実際の開発現場でどのように使われるのでしょうか。

主要なユースケースをいくつか紹介します。

1. オプション設定のデフォルト値

APIから取得したデータや、関数の引数として渡される設定オブジェクトを扱う際、特定のプロパティが省略されている場合があります。

JavaScript
function initializeApp(config) {
  // アニメーション速度設定(0が指定される可能性がある)
  const speed = config.animationSpeed ?? 300;
  
  // 表示モード(空文字が指定される可能性がある)
  const theme = config.themeName ?? "standard";

  console.log(`Setting up with speed: ${speed}, theme: ${theme}`);
}

initializeApp({ animationSpeed: 0, themeName: "" });
initializeApp({});
実行結果
Setting up with speed: 0, theme: 
Setting up with speed: 300, theme: standard

この例では、animationSpeed0が指定された場合でも、その値が尊重されます。

もしここを||で記述していたら、0を指定したのにもかかわらずデフォルトの300が適用されてしまうというバグに繋がります。

2. オプショナルチェーン(?. )との組み合わせ

ヌル値合体演算子は、オプショナルチェーン(?. )と非常に相性が良いのが特徴です。

深い階層にあるプロパティに安全にアクセスしつつ、最終的なデフォルト値を設定するパターンは、モダンJavaScriptの定番と言えます。

JavaScript
const userData = {
  profile: {
    settings: {
      fontSize: 0
    }
  }
};

// 安全にプロパティを辿り、値がなければデフォルト値を設定
const userFontSize = userData.profile?.settings?.fontSize ?? 16;
const userNickName = userData.profile?.nickname ?? "Anonymous";

console.log("Font Size:", userFontSize);
console.log("Nickname:", userNickName);
実行結果
Font Size: 0
Nickname: Anonymous

この組み合わせにより、if文をネストさせることなく、簡潔かつ安全にデータの取得と初期化を完結させることができます。

3. APIレスポンスの処理

外部APIから取得するデータは、必ずしもすべてのフィールドが含まれているとは限りません。

特に「数値型」や「文字列型」のフィールドにおいて、その値が未定義なのか、それともゼロや空文字なのかを区別することは、ビジネスロジック上非常に重要です。

JavaScript
async function fetchUserStatus() {
  // 仮想的なAPIレスポンス
  const response = {
    data: {
      loginCount: 0,
      lastMessage: null
    }
  };

  const loginCount = response.data.loginCount ?? "未計測";
  const lastMessage = response.data.lastMessage ?? "メッセージはありません";

  console.log(`ログイン回数: ${loginCount}`);
  console.log(`最新メッセージ: ${lastMessage}`);
}

fetchUserStatus();
実行結果
ログイン回数: 0
最新メッセージ: メッセージはありません

使用上の注意点と制限事項

非常に便利なヌル値合体演算子ですが、いくつか注意すべきルールがあります。

演算子の優先順位と括弧の必要性

ヌル値合体演算子は、他の論理演算子(&&||)と直接混ぜて使うことはできません。もし混ぜて使おうとすると、構文エラー(SyntaxError)が発生します。

これは、演算の優先順位による混乱を防ぐための言語仕様上の制約です。

JavaScript
// 構文エラーになる例
// const result = a || b ?? c; // Uncaught SyntaxError

// 括弧を使用して優先順位を明示する
const result = (a || b) ?? c;

複数の論理演算子を組み合わせる場合は、必ず括弧を用いて実行順序を明確にする必要があります。

これにより、コードの可読性も向上し、意図しない判定ミスを防ぐことができます。

DOMプロパティへの適用

ブラウザのDOM操作において、textContentvalue プロパティにデフォルト値を設定する場合も注意が必要です。

JavaScript
const inputElement = document.querySelector("#user-input");
const savedValue = localStorage.getItem("input_cache");

// null の場合だけ空文字を設定したい
inputElement.value = savedValue ?? "";

localStorage.getItemはキーが存在しない場合にnullを返すため、??を使うことで「保存された値があればそれを使う、なければ空にする」というロジックが安全に記述できます。

2026年におけるベストプラクティス

現代のJavaScript開発(ECMAScript 2026基準)においては、単に新しい演算子を使うだけでなく、チーム全体でのコーディング規約として使い分けを明確にすることが推奨されます。

論理和(||)を使い続けるべきケース

すべてを??に置き換える必要はありません。

以下のようなケースでは、依然として||が適しています。

  • 「偽値」すべてを排除したい場合:
    値が0であっても""であっても、それらを無効と見なしてデフォルト値を適用したいなら、||の方が記述が短く済みます。
  • レガシーブラウザへの配慮:
    トランスパイラ(Babel等)を使用していない古い環境では??が動作しないため、実行環境の制約がある場合は考慮が必要です(ただし、2026年現在ではほぼすべての環境でネイティブサポートされています)。

ヌル値合体(??)を選択すべきケース

  • 数値(0を含む)を扱う場合:
    カウンターや価格、インデックスなど。
  • 空文字列を有効な入力として扱う場合:
    ユーザー名やコメント欄など。
  • ブール値(falseを含む)を扱う場合:
    フラグ管理やチェックボックスの状態など。

まとめ

JavaScriptのヌル値合体演算子(??)は、従来の論理和演算子||が抱えていた「偽値による意図しない上書き」という問題を解決するために生まれました。

nullまたはundefinedのみを対象にデフォルト値を割り当てるという明確な振る舞いにより、コードの意図がより正確に伝わり、デバッグの難しい細かなバグを未然に防ぐことができます。

最後に、使い分けのポイントを整理します。

  • ?? は、「本当に値が存在しないときだけ」デフォルト値を出したい時に使う。
  • || は、「0や空文字も含めて、何か中身がないと困るとき」に使う。

この使い分けを意識するだけで、あなたの書くJavaScriptコードの堅牢性は飛躍的に向上します。

ぜひ日々の開発で積極的に活用してみてください。