Javascript 空值合并運算符 '??'

2023-02-17 10:37 更新
最近新增的特性

這是一個最近添加到 JavaScript 的特性。 舊式瀏覽器可能需要 polyfills.

空值合并運算符(nullish coalescing operator)的寫法為兩個問號 ??

由于它對待 null 和 undefined 的方式類似,所以在本文中我們將使用一個特殊的術(shù)語對其進行表示。為簡潔起見,當(dāng)一個值既不是 null 也不是 undefined 時,我們將其稱為“已定義的(defined)”。

a ?? b 的結(jié)果是:

  • 如果 ??是已定義的,則結(jié)果為 ?a?,
  • 如果 ??不是已定義的,則結(jié)果為 ?b?。

換句話說,如果第一個參數(shù)不是 null/undefined,則 ?? 返回第一個參數(shù)。否則,返回第二個參數(shù)。

空值合并運算符并不是什么全新的東西。它只是一種獲得兩者中的第一個“已定義的”值的不錯的語法。

我們可以使用我們已知的運算符重寫 result = a ?? b,像這樣:

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

現(xiàn)在你應(yīng)該清楚了 ?? 的作用。讓我們來看看它的使用場景吧。

?? 的常見使用場景是提供默認(rèn)值。

例如,在這里,如果 user 的值不為 null/undefined 則顯示 user,否則顯示 匿名

let user;

alert(user ?? "匿名"); // 匿名(user 未定義)

在下面這個例子中,我們將一個名字賦值給了 user

let user = "John";

alert(user ?? "匿名"); // John(user 已定義)

我們還可以使用 ?? 序列從一系列的值中選擇出第一個非 null/undefined 的值。

假設(shè)我們在變量 firstName、lastName 或 nickName 中存儲著一個用戶的數(shù)據(jù)。如果用戶決定不填寫相應(yīng)的值,則所有這些變量的值都可能是未定義的。

我們想使用這些變量之一顯示用戶名,如果這些變量的值都是 null/undefined,則顯示 “匿名”。

讓我們使用 ?? 運算符來實現(xiàn)這一需求:

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

// 顯示第一個已定義的值:
alert(firstName ?? lastName ?? nickName ?? "匿名"); // Supercoder

與 || 比較

或運算符 || 可以以與 ?? 運算符相同的方式使用。像我們在 上一章 所講的那樣。

例如,在上面的代碼中,我們可以用 || 替換掉 ??,也可以獲得相同的結(jié)果:

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

// 顯示第一個真值:
alert(firstName || lastName || nickName || "Anonymous"); // Supercoder

縱觀 JavaScript 發(fā)展史,或 || 運算符先于 ?? 出現(xiàn)。它自 JavaScript 誕生就存在了,因此開發(fā)者長期將其用于這種目的。

另一方面,空值合并運算符 ?? 是最近才被添加到 JavaScript 中的,它的出現(xiàn)是因為人們對 || 不太滿意。

它們之間重要的區(qū)別是:

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

換句話說,|| 無法區(qū)分 false、0、空字符串 "" 和 null/undefined。它們都一樣 —— 假值(falsy values)。如果其中任何一個是 || 的第一個參數(shù),那么我們將得到第二個參數(shù)作為結(jié)果。

不過在實際中,我們可能只想在變量的值為 null/undefined 時使用默認(rèn)值。也就是說,當(dāng)該值確實未知或未被設(shè)置時。

例如,考慮下面這種情況:

let height = 0;

alert(height || 100); // 100
alert(height ?? 100); // 0
  • ?height || 100? 首先會檢查 ?height? 是否為一個假值,它是 ?0?,確實是假值。
    • 所以,?||? 運算的結(jié)果為第二個參數(shù),?100?。
  • ?height ?? 100? 首先會檢查 ?height ?是否為 ?null/undefined?,發(fā)現(xiàn)它不是。
    • 所以,結(jié)果為 ?height ?的原始值,?0?。

實際上,高度 0 通常是一個有效值,它不應(yīng)該被替換為默認(rèn)值。所以 ?? 運算得到的是正確的結(jié)果。

優(yōu)先級

?? 運算符的優(yōu)先級與 || 相同,它們的的優(yōu)先級都為 4,詳見:MDN。

這意味著,就像 || 一樣,空值合并運算符在 = 和 ? 運算前計算,但在大多數(shù)其他運算(例如 + 和 *)之后計算。

所以我們可能需要在這樣的表達式中添加括號:

let height = null;
let width = null;

// 重要:使用括號
let area = (height ?? 100) * (width ?? 50);

alert(area); // 5000

否則,如果我們省略了括號,則由于 * 的優(yōu)先級比 ?? 高,它會先執(zhí)行,進而導(dǎo)致錯誤的結(jié)果。

// 沒有括號
let area = height ?? 100 * width ?? 50;

// ……將這樣計算(不符合我們的期望):
let area = height ?? (100 * width) ?? 50;

?? 與 && 或 || 一起使用

出于安全原因,JavaScript 禁止將 ?? 運算符與 && 和 || 運算符一起使用,除非使用括號明確指定了優(yōu)先級。

下面的代碼會觸發(fā)一個語法錯誤:

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

這個限制無疑是值得商榷的,它被添加到語言規(guī)范中是為了避免人們從 || 切換到 ?? 時的編程錯誤。

可以明確地使用括號來解決這個問題:

let x = (1 && 2) ?? 3; // 正常工作了

alert(x); // 2

總結(jié)

  • 空值合并運算符 ?? 提供了一種從列表中選擇第一個“已定義的”值的簡便方式。
  • 它被用于為變量分配默認(rèn)值:

    // 當(dāng) height 的值為 null 或 undefined 時,將 height 的值設(shè)置為 100
    height = height ?? 100;
  • ???? 運算符的優(yōu)先級非常低,僅略高于 ??? 和 ?=?,因此在表達式中使用它時請考慮添加括號。
  • 如果沒有明確添加括號,不能將其與 ?||? 或 ?&&? 一起使用。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號