合并空值运算符??
一、介绍
合并空值运算符与null和undefined相似,可以理解为当一个值既不是null或undefined时,我们将其称为’已定义的defined’。
a ?? b的结果是:
● 如果a是已定义的,那么结果就是a,
● 如果a不是已定义的,那么结果就是b
如果第一个参数不是null或undefined,那么??返回第一个参数。否则返回第二个参数。
例如,我们用已知的三目运算符来执行一下:
result =(a!==null && a!==undefined) ?a:b //那么就可以简写成 a??b 来判断a是否是一个未定义的值
使用场景:
在这里,如果 user 的值不为 null/undefined 则显示 user,否则显示 匿名:
let user;
alert(user ?? "匿名"); //'匿名'
在下面这个例子中,我们将一个名字赋值给了 user:
let user = '张三'
alert(user ?? "匿名"); //张三
我们还可以从一系列的值中选择出第一个已定义的值(非 null/undefined 的值)。
let firstName = null;
let lastName = null;
let nickName = "张三"
//显示第一个已定义的值
alert(firstName ?? lastName ?? nickName ?? "匿名" ) //张三
二、与 || 比较
或运算符 || 可以以与 ?? 运算符相同的方式使用。
let firstName = null;
let lastName = null;
let nickName = "张三"
//显示第一个已定义的值
alert(firstName || lastName || nickName || "匿名" ) //张三
区别:
| | :返回第一个 真 值
? ? :返回第一个 已定义的 值
|| 无法区分 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,不是null/undefined,所以结果为height的原始值0
小节:实际上,高度 0 通常是一个有效值,它不应该被替换为默认值。所以 ?? 运算得到的是正确的结果。
三、优先级
?? 运算符的优先级与 || 相同,它们的优先级都为 3,详见:MDN。
这意味着,就像 || 一样,空值合并运算符在 = 和 ? 运算前计算,但在大多数其他运算(例如 + 和 *)之后计算。
// 所以我们可能需要在这样的表达式中添加括号:
let height = null;
let width = null;
// 重要:使用括号
let area = (height ?? 100) * (width ?? 50);
alert(area); // 5000
否则,如果我们省略了括号,则由于 * 的优先级比 ?? 高,它会先执行,进而导致错误的结果。\
// 没有括号
let area = height ?? 100 * width ?? 50;
// ……将这样计算(不符合我们的期望):
let area = height ?? (100 * width) ?? 50;
?? 与 && 或 || 一起使用
出于安全原因,JavaScript 禁止将 ?? 运算符与 && 和 || 运算符一起使用,除非使用括号明确指定了优先级。
//会触发错误
let x = 1 && 2 ?? 3; // Syntax error
这个限制无疑是值得商榷的,它被添加到语言规范中是为了避免人们从 || 切换到 ?? 时的编程错误。
可以明确地使用括号来解决这个问题:
let x = (1 && 2) ?? 3; // 正常工作了
alert(x); // 2
四、总结
- 合并空值运算符提供一种从列表中选择第一个"已定义"的值的简便方式,用于为变量分配默认值。
- ?? 运算符的优先级非常低,仅略高于 ? 和 =,因此在表达式中使用它时请考虑添加括号。
- 如果没有明确添加括号,不能将其与 || 或 && 一起使用。