ES2020 链判断与 Null 判断运算符详解

作者:木棉

时间:2026-10-24

浏览:164

本文深入解析 ES2020 引入的链判断运算符 (?.) 和 Null 判断运算符 (??)。详细阐述 ?. 在对象属性访问、方法调用中的短路机制及括号影响,以及 ?? 在指定默认值时忽略 falsy 值的特性。通过代码示例对比传统写法,说明两者配合使用的优势及优先级规则,帮助开发者编写更简洁安全的 JavaScript 代码。

文章目录

链判断运算符(?.)

该运算符非常实用且常用,若与 Null 判断运算符结合使用,效果更佳,表现完美。

接下来通过代码示例进行演示:

在常规开发中,获取对象多层嵌套属性时,通常需要进行多次非空判断。若不进行判断,一旦中间某一层为空,后续操作将直接报错,导致程序中断。


// error
const firstName = message.body.user.firstName;

// ok
const firstName = (message
 && message.body
 && message.body.user
 && message.body.user.firstName) || 'default';

虽然可以使用三元运算符 ?: 来对单个值进行判断,如下例所示,必须先确认 fooInput 存在,才能读取其 value 属性。


const fooInput = myForm.querySelector('input[name=foo]')
const fooValue = fooInput ? fooInput.value : undefined

随后,我们使用 ?. 运算符来展示其便捷性:


const firstName = message?.body?.user?.firstName || 'default';
const fooValue = myForm.querySelector('input[name=foo]')?.value

该运算符使得代码一气呵成,它直接在链式调用过程中判断左侧对象是否为 null 或 undefined。若为真,则停止后续运算并直接返回 undefined。

链判断运算符主要包含三种使用场景。

  • obj?.prop // 访问对象属性
  • obj?.[expr] // 同上,通过表达式访问
  • func?.(...args) // 调用函数或对象方法

"#C0FFEE".match(/#([A-Z]+)/i)?.[1] // 无匹配返回null,匹配则返回一个数组

a?.b
// 等同于
a == null ? undefined : a.b

a?.[x]
// 等同于
a == null ? undefined : a[x]

a?.b()
// 等同于
a == null ? undefined : a.b()

a?.()
// 等同于
a == null ? undefined : a()

在上述代码中,需特别注意后两种形式。若 a?.b() 中的 a.b 并非函数,则 a?.b() 会抛出错误;同理,若 a 不是 null 或 undefined,但也不是函数,则 a?.() 也会报错。

使用该运算符时,有几个关键点需要注意。

(1)短路机制

?. 运算符本质上是一种短路机制,一旦条件不满足(即左侧为 null 或 undefined),后续操作将不再执行。


a?.[++x]
// 等同于
a == null ? undefined : a[++x]

如上例所示,若 a 为 undefined 或 null,则 x 不会进行递增运算。这意味着链判断运算符一旦判定左侧为 null 或 undefined,右侧的表达式将不再求值。

(2)delete 运算符


delete a?.b
// 等同于
a == null ? undefined : delete a.b

如上例所示,若 a 为 undefined 或 null,表达式将直接返回 undefined,而不会执行 delete 运算。

(3)括号的影响

若属性链中包含圆括号,链判断运算符仅对圆括号内部生效,对外部无影响。


(a?.b).c
// 等价于
(a == null ? undefined : a.b).c

如上例所示,?. 对圆括号外部没有影响,无论 a 对象是否存在,圆括号后的 .c 属性访问总会执行。

通常情况下,使用 ?. 运算符时,应避免使用圆括号。

(4)报错场合

以下写法是被禁止的,会导致报错。


// 构造函数
new a?.()
new a?.b()

// 链判断运算符的右侧有模板字符串
a?.`{b}`
a?.b`{c}`

// 链判断运算符的左侧是 super
super?.()
super?.foo

// 链运算符用于赋值运算符左侧
a?.b = c

(5)右侧不得为十进制数值

为了兼容旧代码,允许 foo?.3:0 被解析为 foo ? .3 : 0。因此规定,如果 ?. 后面紧跟一个十进制数字,?. 不再被视为完整运算符,而是按三元运算符处理,即小数点归属于后面的十进制数字,形成一个小数。

Null 判断运算符(??)

在读取对象属性时,若某属性值为 null 或 undefined,有时需要为其指定默认值。常见做法是通过 || 运算符指定默认值。


const headerText = response.settings.headerText || 'Hello, world!';
const animationDuration = response.settings.animationDuration || 300;
const showSplashScreen = response.settings.showSplashScreen || true;

上述三行代码均通过 || 运算符指定默认值,但若属性值为 ''、false 或 0,默认值也会生效,这往往不符合预期。

为避免这种情况,ES2020 引入了一个新的 Null 判断运算符 ??。其行为类似 ||,但仅当运算符左侧的值为 null 或 undefined 时,才返回右侧的值。


const headerText = response.settings.headerText ?? 'Hello, world!';
const animationDuration = response.settings.animationDuration ?? 300;
const showSplashScreen = response.settings.showSplashScreen ?? true;

如上例所示,默认值仅在左侧属性值为 null 或 undefined 时才会生效。

该运算符的一个主要目的,就是与链判断运算符 ?. 配合使用,为 null 或 undefined 的值设置默认值。


const animationDuration = response.settings?.animationDuration ?? 300;

如上例所示,如果 response.settings 是 null 或 undefined,或者 response.settings.animationDuration 是 null 或 undefined,就会返回默认值 300。也就是说,这一行代码涵盖了双重属性的判断。

该运算符非常适合用于判断函数参数是否已赋值:


function Component(props) {
 const enable = props?.enable ?? true;
 // …
}

// 上面代码等同于
function Component(props) {
 const {
  enable: enable = true,
 } = props;
 // …
}

?? 存在运算优先级问题,需明确其与 && 和 || 的优先级高低。当前规则是,若多个逻辑运算符一起使用,必须用括号明确优先级,否则会报错。


// 报错
lhs && middle ?? rhs
lhs ?? middle && rhs
lhs || middle ?? rhs
lhs ?? middle || rhs

上述四个表达式均会报错,必须加入表明优先级的括号。


(lhs && middle) ?? rhs;
lhs && (middle ?? rhs);

(lhs ?? middle) && rhs;
lhs ?? (middle && rhs);

(lhs || middle) ?? rhs;
lhs || (middle ?? rhs);

(lhs ?? middle) || rhs;
lhs ?? (middle || rhs);

ES 版本更新的背景与福利

在几个月前的腾讯开发者大会上,有位热心开发者向 ES 标准委员会成员请教了此问题,借此文章话题,在此分享给大家。

关于 ES 版本更新,为何从 ES5 到 ES6 是大幅度的书籍式更新,而此后版本仅新增少数几个新技术?

原因在于:“怕开发者学不动!”

实际上,前者的更新方式涉及大量内容,厚如天书,容易让人望而却步,尚未入门便已感到压力。

而后者的更新仅涉及少数新技术,可能只需半天或一天即可完全掌握并应用,获得的成就感更多且更快。

到此为止,关于 ES2020 让代码更优美的运算符 (?.) (??) 的文章就介绍到这里,更多相关 ES2020 运算符 (?.) (??) 内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章