2023 年 4 月 23 日

空值連接運算子「??」

最新新增內容
這是最近新增到語言中的內容。舊瀏覽器可能需要 多重載入

空值連接運算子寫成兩個問號 ??

由於它以相同的方式處理 nullundefined,我們將在本文中使用一個特殊術語。為簡潔起見,當一個值既不是 null 也不是 undefined 時,我們將說該值是「已定義」。

a ?? b 的結果是

  • 如果 a 已定義,則為 a
  • 如果 a 未定義,則為 b

換句話說,?? 會傳回第一個引數,如果它不是 null/undefined 的話。否則,傳回第二個引數。

空值連接運算子並不是什麼完全新穎的東西。它只是一個漂亮的語法,用於取得兩個值中的第一個「已定義」值。

我們可以使用我們已經知道的運算子來改寫 result = a ?? b,如下所示

result = (a !== null && a !== undefined) ? a : b;

現在應該絕對清楚 ?? 的作用了。讓我們看看它在哪裡有幫助。

?? 的常見用途是提供預設值。

例如,如果 user 的值不是 null/undefined,我們會顯示 user,否則顯示 Anonymous

let user;

alert(user ?? "Anonymous"); // Anonymous (user is undefined)

以下是將 user 指定給名稱的範例

let user = "John";

alert(user ?? "Anonymous"); // John (user is not null/undefined)

我們也可以使用一系列 ?? 從不是 null/undefined 的清單中選取第一個值。

假設我們在變數 firstNamelastNamenickName 中有使用者的資料。如果使用者決定不填入對應的值,它們都可能未定義。

我們希望使用其中一個變數顯示使用者名稱,或者如果它們都是 null/undefined,則顯示「Anonymous」。

讓我們使用 ?? 運算子來執行此操作

let firstName = null;
let lastName = null;
let nickName = "Supercoder";

// shows the first defined value:
alert(firstName ?? lastName ?? nickName ?? "Anonymous"); // Supercoder

與 || 比較

OR || 運算子可以與 ?? 以相同的方式使用,如 前一章節 所述。

例如,在上面的程式碼中,我們可以用 || 取代 ??,仍然可以得到相同的結果

let firstName = null;
let lastName = null;
let nickName = "Supercoder";

// shows the first truthy value:
alert(firstName || lastName || nickName || "Anonymous"); // Supercoder

從歷史上來看,OR || 運算子是最早出現的。它從 JavaScript 的開端就存在,因此開發人員長期以來都將它用於此類目的。

另一方面,nullish 合併運算子 ?? 直到最近才加入 JavaScript,而原因是人們對 || 不太滿意。

它們之間的重要區別在於

  • || 傳回第一個 真值
  • ?? 傳回第一個 已定義 的值。

換句話說,|| 不區分 false0、空字串 ""null/undefined。它們都是相同的 - 假值。如果其中任何一個是 || 的第一個參數,那麼我們將得到第二個參數作為結果。

然而,在實務上,我們可能只想在變數為 null/undefined 時使用預設值。也就是說,當值確實未知/未設定時。

例如,考慮以下情況

let height = 0;

alert(height || 100); // 100
alert(height ?? 100); // 0
  • height || 100 檢查 height 是否為假值,而它是 0,確實為假值。
    • 因此 || 的結果是第二個參數 100
  • height ?? 100 檢查 height 是否為 null/undefined,而它不是,
    • 因此結果是「原樣」的 height,也就是 0

實際上,零高度通常是有效值,不應替換為預設值。因此 ?? 執行的是正確的動作。

優先順序

?? 算子的優先順序與 || 相同。它們在 MDN 表格 中都等於 3

這表示與 || 一樣,空值合併算子 ?? 會在 =? 之前評估,但在 +* 等大多數其他運算之後評估。

因此我們可能需要在類似這樣的表達式中加入括號

let height = null;
let width = null;

// important: use parentheses
let area = (height ?? 100) * (width ?? 50);

alert(area); // 5000

否則,如果我們省略括號,由於 * 的優先順序高於 ??,因此它會先執行,導致結果不正確。

// without parentheses
let area = height ?? 100 * width ?? 50;

// ...works this way (not what we want):
let area = height ?? (100 * width) ?? 50;

將 ?? 與 && 或 || 搭配使用

由於安全原因,JavaScript 禁止將 ??&&|| 算子一起使用,除非優先順序已明確指定括號。

以下程式碼會觸發語法錯誤

let x = 1 && 2 ?? 3; // Syntax error

這個限制當然有爭議,它被加入語言規格中,目的是為了避免程式設計錯誤,當人們開始從 || 切換到 ?? 時。

使用明確的括號來解決它

let x = (1 && 2) ?? 3; // Works

alert(x); // 2

摘要

  • 空值合併算子 ?? 提供了一種簡短的方法,可從清單中選擇第一個「已定義」的值。

    它用於將預設值指定給變數

    // set height=100, if height is null or undefined
    height = height ?? 100;
  • 算子 ?? 的優先順序非常低,僅比 ?= 高一點,因此在表達式中使用它時,請考慮加入括號。

  • 禁止在沒有明確括號的情況下將它與 ||&& 一起使用。

淺色主題深色主題

留言

在評論前請先閱讀…
  • 如果你有改善建議 - 請 提交 GitHub 議題 或提交拉取請求,而不是留言。
  • 如果你無法理解文章中的某個部分 – 請詳細說明。
  • 若要插入少數幾個字的程式碼,請使用 <code> 標籤,若要插入多行程式碼 – 請將它們包覆在 <pre> 標籤中,若要插入超過 10 行程式碼 – 請使用沙盒 (plnkrjsbincodepen…)