# 0やfalseが初期値に変わる理由｜JavaScriptの||と??の使い分け

0を指定したのに3になる、falseにしたのに有効になる。||と??の違いを、空文字列・null・undefined・NaNも含めて確認。初期値の補完と入力チェックを分ける実行例を紹介します。

正規URL: https://3mikan.com/archives/6087

著者: みかん
公開: 2026-09-08T15:06:00.000Z
更新: 2026-09-08T15:06:00.000Z

再試行を無効にしたくて回数に`0`を指定したのに、初期値の`3`へ戻ってしまう。そんなときは、値の読み取り方だけでなく、`value || 3`という補完処理を確認してみてください。

`||`は`0`や`false`、空文字列も右辺へ置き換えます。**`null`と`undefined`だけを補いたいなら`??`を使う**と、指定済みの`0`や`false`を残せます。ただし、`??`は入力値が正しいかを調べる道具ではありません。

## まずは手元で再現する

[実行用サンプルを開く・保存する](/examples/default-value-boundaries.mjs)から、`default-value-boundaries.mjs`という名前で保存して実行してください。

```sh
node default-value-boundaries.mjs
```

確認環境はNode.js v24.14.0です。追加パッケージ、アカウント、ネットワーク通信、ファイルの書き込みは不要です。サンプルは正常例と反例を照合し、最後に「すべての実行例が期待値と一致しました。」と表示します。以下のJavaScriptの枠も、それぞれ別の`.mjs`ファイルで実行できます。

## ||は「未設定」だけを調べているのではない

右辺を`"default"`にしたとき、結果は次のように分かれます。

| 左辺の値 | `値 \|\| "default"` | `値 ?? "default"` |
| --- | --- | --- |
| `0` | `"default"` | `0` |
| `false` | `"default"` | `false` |
| `""` | `"default"` | `""` |
| `null` | `"default"` | `"default"` |
| `undefined` | `"default"` | `"default"` |
| `NaN` | `"default"` | `NaN` |
| `"0"` | `"0"` | `"0"` |

`||`は左辺を真偽として判定し、偽なら右辺を選びます。表の`0`、`false`、空文字列などがこの対象です。一方、`??`は左辺が`null`か`undefined`のときだけ右辺を選びます。どちらも、選んだ値そのものを返すため、結果が必ず真偽値になるわけではありません。[ECMAScriptの評価規則](https://tc39.es/ecma262/2025/multipage/ecmascript-language-expressions.html#sec-binary-logical-operators)

たとえば`false ?? true`は`false`です。「通知を無効にする」という明示的な設定を保持できます。逆に、空の表示名を代替表示へ変えたい処理なら、空文字列でも右辺を選ぶことが目的に合う場合があります。

## 初期値を入れてから、使える値か確認する

再試行回数について、「未指定と`null`は3回、0以上の整数なら指定どおり、それ以外はエラー」と決めてみます。これはこの設定項目で採用する方針です。

```js
function retryCount(value) {
  const count = value ?? 3;
  if (!Number.isInteger(count) || count < 0) {
    throw new TypeError('回数は0以上の整数にしてください');
  }
  return count;
}

console.log(retryCount(0));         // 0
console.log(retryCount(undefined)); // 3
console.log(retryCount(null));      // 3

try {
  retryCount('0');
} catch (error) {
  console.log(error.name); // TypeError
}
```

`0`は残りますが、文字列の`"0"`は受け付けません。サンプルでは、負の数、小数、`NaN`、`Infinity`、空文字列、`false`も拒否することを確認しています。

`NaN ?? 3`の結果は`NaN`のままです。`??`に置き換えるだけで不正な計算結果まで直るわけではありません。値を補う条件と、補った後に許可する型・範囲を分けると、どちらの処理を変えるべきかが明確になります。[MDNのnull合体演算子の説明](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing)

## nullに意味があるなら、まとめて補わない

APIや設定によっては、`null`が「不明」や「明示的に空」を表すことがあります。その意味を残したいなら、`??`で初期値に変えるのは適切ではありません。

```js
const preserveNull = (value) => value === undefined ? 3 : value;
console.log(preserveNull(null));      // null
console.log(preserveNull(undefined)); // 3

console.log(Object.hasOwn({ count: undefined }, 'count')); // true
console.log(Object.hasOwn({}, 'count'));                 // false
```

前半は`undefined`だけを補う例です。後半は「キーは存在するが値が`undefined`」と「キー自体がない」を分ける例です。どちらも単に`obj.count ?? 3`とすると同じ3になります。区別が必要な処理では、値だけでなくキーの有無も確認します。

保存前後にこうした区別が失われる問題は、[JSONへの保存と読み戻しで変わる値](/archives/6084)でも扱っています。読み取り後の補完では、保存時に失った情報までは戻せません。

## 右辺の処理は、必要な場合だけ動く

初期値を作る関数を右辺に置いた場合も、`null`か`undefined`のときだけ呼ばれます。

```js
let called = 0;
const fallback = () => { called += 1; return 3; };

console.log(0 ?? fallback());    // 0
console.log(called);            // 0
console.log(null ?? fallback()); // 3
console.log(called);             // 1
```

`||`や`&&`と`??`を括弧なしで混ぜると、構文エラーになります。たとえば`null || 3 ?? 4`は書けません。`(null || 3) ?? 4`のように、どこを先に判定するかを明示します。これは見た目の推奨ではなく、言語の構文上の規則です。[ECMAScriptの構文と評価規則](https://tc39.es/ecma262/2025/multipage/ecmascript-language-expressions.html#sec-binary-logical-operators)

## 置き換える前に、残したい値を決める

`0`や`false`が有効な設定なら、その値を未設定扱いにしないことが出発点です。`null`と`undefined`を同じ未設定とするなら`??`、`null`を残すなら明示的な条件分岐を使います。

その後で、数値の範囲や文字列の形式を確認します。`||`をすべて`??`に置き換えるのではなく、その項目にとって「未設定」と「不正な値」が何かを決めると、初期値が意図せず上書きされる問題を直せます。

## 確認した一次情報

- [ECMAScript 2025: 論理演算とnull合体演算の評価](<https://tc39.es/ecma262/2025/multipage/ecmascript-language-expressions.html#sec-binary-logical-operators>): 確認日 2026-09-08
- [MDN: null合体演算子](<https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Nullish_coalescing>): 確認日 2026-09-08
