# 【JS】?? 與  ||  比較

在 JavaScript 中，`??` (Nullish Coalescing) 和 `||` (Logical OR) 看起來很像，都是用來提供**預設值**的運算子，但它們判斷「什麼時候該用預設值」的標準完全不同。

這篇教學將幫你釐清兩者的核心差異，並告訴你什麼時候該用哪一個。

<div aria-busy="false" aria-live="polite" id="bkmrk-"><div aria-busy="false" aria-live="polite">---

</div></div>## 核心差異：判斷標準

兩者的主要區別在於對於「虛值 (Falsy values)」的處理方式。

### 1. 邏輯或運算子 `||` (Logical OR)

只要左側的值轉換為布林值後是 false，就會回傳右側的值。

JavaScript 中的 Falsy 值包括：

<div aria-busy="false" aria-live="polite" id="bkmrk-null-undefined-0-%22%22-"><div aria-busy="false" aria-live="polite">- `null`
- `undefined`
- `0`
- `""` (空字串)
- `NaN`
- `false`

</div></div>### 2. 空值合併運算子 `??` (Nullish Coalescing)

只有當左側的值是 null 或 undefined 時，才會回傳右側的值。

它會保留 0、"" 和 false 等有效數據。

<div aria-busy="false" aria-live="polite" id="bkmrk--0"><div aria-busy="false" aria-live="polite">---

</div></div>## 對比表

<table border="1" id="bkmrk-%E7%89%B9%E6%80%A7-%7C%7C-%28or%29-%3F%3F-%28nulli"><thead><tr><th>特性</th><th>|| (OR)</th><th>?? (Nullish)</th></tr></thead><tbody><tr><td>**觸發預設值的條件**</td><td>任何 Falsy 值 (0, "", false...)</td><td>僅限 null 或 undefined</td></tr><tr><td>**對 0 的處理**</td><td>視為 False，會被預設值覆蓋</td><td>視為有效值，保留 0</td></tr><tr><td>**對 "" (空字串) 的處理**</td><td>視為 False，會被預設值覆蓋</td><td>視為有效值，保留 ""</td></tr><tr><td>**設計目的**</td><td>廣泛的邏輯備案</td><td>精確處理「缺失資料」</td></tr></tbody></table>

<div aria-busy="false" aria-live="polite" id="bkmrk--1"><div aria-busy="false" aria-live="polite">---

</div></div>## 實戰範例

假設我們正在開發一個使用者設定頁面：

<div aria-busy="false" aria-live="polite" id="bkmrk-javascript-const-use"><div aria-busy="false" aria-live="polite"><div data-hveid="0" data-ved="0CAAQhtANahcKEwjVvbzE0eGRAxUAAAAAHQAAAAAQWA"><div><span class="ng-tns-c3121828532-21">JavaScript</span><div><button aria-label="複製代碼" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c3121828532-21 mat-unthemed ng-star-inserted"></button></div></div><div><div>```
<span class="hljs-keyword">const</span> userSettings = {
  <span class="hljs-attr">speed</span>: <span class="hljs-number">0</span>,
  <span class="hljs-attr">nickname</span>: <span class="hljs-string">""</span>,
  <span class="hljs-attr">isPremium</span>: <span class="hljs-literal">false</span>,
  <span class="hljs-attr">theme</span>: <span class="hljs-literal">null</span>
};

<span class="hljs-comment">// --- 使用 || 的結果 ---</span>
<span class="hljs-built_in">console</span>.log(userSettings.speed || <span class="hljs-number">10</span>);      <span class="hljs-comment">// 輸出: 10 (即便 0 是有效的數值)</span>
<span class="hljs-built_in">console</span>.log(userSettings.nickname || <span class="hljs-string">"匿名"</span>); <span class="hljs-comment">// 輸出: "匿名" (即便空字串是有效的名字)</span>
<span class="hljs-built_in">console</span>.log(userSettings.isPremium || <span class="hljs-literal">true</span>);  <span class="hljs-comment">// 輸出: true (這會讓使用者的 false 設定失效)</span>

<span class="hljs-comment">// --- 使用 ?? 的結果 ---</span>
<span class="hljs-built_in">console</span>.log(userSettings.speed ?? <span class="hljs-number">10</span>);      <span class="hljs-comment">// 輸出: 0 (正確保留了數值)</span>
<span class="hljs-built_in">console</span>.log(userSettings.nickname ?? <span class="hljs-string">"匿名"</span>); <span class="hljs-comment">// 輸出: "" (正確保留了空字串)</span>
<span class="hljs-built_in">console</span>.log(userSettings.isPremium ?? <span class="hljs-literal">true</span>);  <span class="hljs-comment">// 輸出: false (正確保留了布林值)</span>
<span class="hljs-built_in">console</span>.log(userSettings.theme ?? <span class="hljs-string">"Light"</span>);  <span class="hljs-comment">// 輸出: "Light" (因為是 null)</span>

```

</div></div></div>---

</div></div>## 使用場景建議

### 什麼時候使用 `??`？ (推薦做法)

當你想要\*\*「安全地提供預設值」\*\*且不希望意外覆蓋掉 `0`、`false` 或 `""` 時。

<div aria-busy="false" aria-live="polite" id="bkmrk-%E8%99%95%E7%90%86-api-%E5%9B%9E%E5%82%B3%E7%9A%84%E9%81%B8%E6%93%87%E6%80%A7%E5%8F%83%E6%95%B8%E3%80%82-%E6%95%B8%E5%80%BC%E8%A8%88"><div aria-busy="false" aria-live="polite">- 處理 API 回傳的選擇性參數。
- 數值計算（例如：計數器、座標）。
- UI 表單輸入（允許空字串）。

</div></div>### 什麼時候使用 `||`？

當你想要\*\*「只要沒內容就給預設值」\*\*，或者你確定資料只會是字串/物件，且你不需要處理 `0` 或空字串的情況。

<div aria-busy="false" aria-live="polite" id="bkmrk-%E8%88%8A%E7%89%88%E7%80%8F%E8%A6%BD%E5%99%A8%E7%9B%B8%E5%AE%B9%E6%80%A7%EF%BC%88%3F%3F-%E6%98%AF-es2020"><div aria-busy="false" aria-live="polite">- 舊版瀏覽器相容性（`??` 是 ES2020 引入的）。
- 確保變數一定有「實質內容」（非空、非零）。

---

</div></div>## 注意事項：混合使用

在 JavaScript 中，出於運算優先級的安全性考慮，你**不能**在沒有括號的情況下直接混合使用 `&&`、`||` 與 `??`：

<div aria-busy="false" aria-live="polite" id="bkmrk-javascript-%2F%2F-%E2%9D%8C-%E9%8C%AF%E8%AA%A4%E8%AA%9E%E6%B3%95"><div aria-busy="false" aria-live="polite"><div data-hveid="0" data-ved="0CAAQhtANahcKEwjVvbzE0eGRAxUAAAAAHQAAAAAQWQ"><div><span class="ng-tns-c3121828532-22">JavaScript</span><div><button aria-label="複製代碼" class="mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-mdc-tooltip-trigger copy-button ng-tns-c3121828532-22 mat-unthemed ng-star-inserted"></button></div></div><div><div>```
<span class="hljs-comment">// ❌ 錯誤語法：SyntaxError</span>
<span class="hljs-comment">// const result = a || b ?? c;</span>

<span class="hljs-comment">// ✅ 正確語法：使用括號明確優先級</span>
<span class="hljs-keyword">const</span> result = (a || b) ?? c;

```

</div></div></div></div></div>> **總結建議：** 在現代開發中，為了避免處理數值 `0` 或布林值時出現非預期的 Bug，**優先考慮使用 `??`**。

需要我為你示範如何將現有的舊代碼從 `||` 重構成 `??` 嗎？