在本教程中,您将了解 JavaScript 空值合并运算符 ??
,它接受两个值并在第一个值是 null
或者 undefined
时返回第二个值 。

ES2020 引入了由双问号 ??
表示的空值合并运算符。
a ?? b
由于它将 null
和 undefined
视为类似的情况,在本文中我们使用一个特殊的术语。为了简洁起见,我们将值称为“已定义”,当它既不是 null
也不是 undefined
时。
a ?? b
的结果是:
-
如果 a 已定义,则返回 a;
-
如果 a 未定义,则返回 b。
换句话说,??
返回第一个非 null
/undefined
的值,否则返回第二个。
空值合并运算符并不是什么全新的概念,它只是一个简洁的语法,用来获取两个值中的第一个“已定义”的值。
我们可以使用已经知道的运算符重写 result = a ?? b
,如下所示:
result = (a !== null && a !== undefined) ? a : b;
现在应该完全清楚 ??
的作用了。让我们看看它在哪里能够派上用场。
??
的常见用法是提供默认值。
例如,下面的代码会显示用户的值(如果它不是 null
或 undefined
),否则显示 “Anonymous”:
let user; alert(user ?? "Anonymous"); // Anonymous (user is undefined)
这是一个用户已分配名字的示例:
let user = "Alice"; let userName = user ?? "Anonymous"; alert(userName); // 输出 "Alice"
我们也可以使用一系列 ??
来从一组值中选择第一个非 null
/undefined
的值。
假设我们有用户的数据存储在变量 firstName
、lastName
或 nickName
中。如果用户没有填写对应的值,这些变量可能都是未定义的。
我们想要显示用户的名字,使用这些变量中的一个,如果它们都为 null
/undefined
,则显示 “Anonymous”。
我们可以使用 ??
运算符来实现这一点:
let firstName = null; let lastName = undefined; let nickName = "Alice"; let userName = firstName ?? lastName ?? nickName ?? "Anonymous"; alert(userName); // 输出 "Alice"
与 ||
的比较
OR
(||
)运算符可以像 ??
一样使用,正如前一章所描述的那样。
例如,在上面的代码中,我们可以将 ??
替换为 ||
,并仍然得到相同的结果:
let firstName = null; let lastName = undefined; let nickName = "Alice"; let userName = firstName || lastName || nickName || "Anonymous"; alert(userName); // 输出 "Alice"
然而,||
和 ??
在处理值时略有不同。||
会认为所有假值(如 0
、false
、""
、NaN
等)都需要被替换,而 ??
只会在遇到 null
或 undefined
时才进行替换。
换句话说,||
不区分 false
、0
、空字符串 ""
和 null
/undefined
,它们都是“假值”(falsy values)。如果这些中的任何一个是 ||
的第一个操作数,那么我们将得到第二个操作数作为结果。
然而,在实际应用中,我们可能只希望在变量为 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
。
在实际应用中,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
短路空值合并运算符
跟逻辑或和与运算符类似,如果第一个操作数不是undefined
或者是 null
,空值合并运算符也不计算第二个值。“短路空值合并运算符” 是对 ??
运算符的一种描述,强调它的短路特性。与其他逻辑运算符(如 &&
和 ||
)一样,??
运算符也具有短路行为,即当左侧操作数已确定结果时,右侧操作数不会被计算。
例如:
let user = null; let userName = user ?? "Anonymous"; // user 为 null,返回 "Anonymous" console.log(userName); // 输出 "Anonymous" user = "Alice"; userName = user ?? "Anonymous"; // user 已定义,返回 "Alice" console.log(userName); // 输出 "Alice"
在上面的例子中,user ?? "Anonymous"
会先检查 user
是否为 null
或 undefined
。如果 user
为 null
或 undefined
,则返回默认值 "Anonymous"
;否则,返回 user
本身的值。所以它是一个短路操作,只有当左侧值为 null
或 undefined
时,右侧的默认值才会被使用。
总结
-
空值合并运算符
??
提供了一种简洁的方式,从一组值中选择第一个“已定义”的值。 -
它用于为变量赋予默认值:
// set height=100, if height is null or undefined height = height ?? 100;
- 运算符
??
的优先级很低,仅略高于?
和=
,因此在表达式中使用时考虑添加括号。 - 在没有显式括号的情况下,禁止将其与
||
或&&
一起使用。