Appearance
链判断运算符
本节目标:理解“链判断运算符”的核心思路,并能把它用于实际开发或面试表达。
[ES2020](https://github.com/tc39/proposal-optional-chaining) 引入了“链判断运算符”(optional chaining operator)**?.**,简化上面的写法。
const firstName = message?.body?.user?.firstName || 'default';const fooValue = myForm.querySelector('input[name=foo]')?.value
使用?.运算符会判断运算符左侧的对象是否为null或undefined,如果是的话就不再往下运算,并返回undefined。链判断运算符有三种用法。obj?.prop // 对象属性obj?.[expr] // 同上func?.(...args) // 函数或对象方法的调用下面是判断对象方法是否存在,如果存在就立即执行的例子。
iterator.return?.()
上面代码中,iterator.return如果有定义,就会调用该方法,否则直接返回undefined。
对于那些可能没有实现的方法,这个运算符尤其有用。
if (myForm.checkValidity?.() === false) { // 表单校验失败 return;}
上面代码中,老式浏览器的表单可能没有checkValidity这个方法,这时?.运算符就会返回undefined,判断语句就变成了undefined === false,所以就会跳过下面的代码。常见形式与旧写法 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?.()也是如此,如果a不是null或undefined,但也不是函数,那么a?.()会报错。
**注意点**
(1)短路机制
a?.[++x]// 等同于a == null ? undefined : a[++x]
上面代码中,如果a是undefined或null,那么x不会进行递增运算。也就是说,链判断运算符一旦为真,右侧的表达式就不再求值。
(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}`
// 链判断运算符的左侧是 supersuper?.()super?.foo
// 链运算符用于赋值运算符左侧a?.b = c
(5)右侧不得为十进制数值
为了保证兼容以前的代码,允许foo?.3:0被解析成foo ? .3 : 0,因此规定如果?.后面紧跟一个十进制数字,那么?.不再被看成是一个完整的运算符,而会按照三元运算符进行处理,也就是说,那个小数点会归属于后面的十进制数字,形成一个小数。