ES2020 链判断与 Null 判断运算符详解
本文深入解析 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 运算符 (?.) (??) 内容请搜索本站以前的文章或继续浏览下面的相关文章,希望大家以后多多支持本站!
JS中将数组和对象混合组装为JSON字符串
2026-11-05 16:41:42
JS运算符优先级与结合性的区别是什么?
2026-11-05 13:22:02
JS Map怎么只遍历Key或Value
2026-11-05 13:00:36
D3.js 与 ECharts 选型对比:灵活性 vs 开发效率
2026-11-05 09:45:01
为什么JS中typeof []返回object:数组类型检测误区解析
2026-11-04 19:50:47
