ES6-链判断运算符
日常业务中,经常会遇到这么一个情况:需要用到的某个属性在接口返回的数据中可能是不存在的或者其上一级的属性是不存在的,例如:
1 const zs = { 2 info : { 3 name : { 4 firstName : "张", 5 lastName : "三" 6 }, 7 } 8 } 9 zs.info.name.firstName // 张
如果数据是齐全的情况下,通过 zs.info.name.firstName可以正常获取到“张”,此时如果name这个字段没有,而直接访问这个字段就会报一个非常常见的错误:Cannot read property 'xxx' of undefined,意思是无法在undefined上读取属性"xxx"。
我们在日常解决这个问题比较安全的写法是:
(zs && zs.info && zs.info.name && zs.info.name.firstName) || "default"
上面判断了四次,看每一层是否有值,
或者也可以用三目运算也可以判断.
ES2020引入了“链判断运算符”(?.),简化了上面的写法
1 const zs = { 2 info : { 3 name : { 4 firstName : "张", 5 lastName : "三" 6 }, 7 } 8 } 9 10 const fn = zs?.info?.name?.lastName || "四"
说明:
- ?.运算符直接在链式调用的时候判断,左侧的对象是否为null或undefined,是的话不再往下运算,而是返回undefined
或者可以用来判断对象中是否有某个方法:
1 const o = { 2 add: () => { console.log(123) } 3 } 4 o.add?.() // 123
上面代码中,o.add方法如果定义了,就会调用该方法,否则,不再执行?.后面的部分
链判断运算符有三种用法:
- obj?.prop // 对象属性
- obj?.[expr] // 同上
- func?.(...args) // 函数或对象方法的调用
obj?.[expr]的一个例子:
1 var str="The rain in SPAIN stays mainly in the plain" 2 let h = str.match(/ain/)?.[1]
上面代码中,判断是否有匹配/ain/正则,如果匹配h 等于匹配的字符串。否则等于undefined
以下是?.运算符常见形式,以及不适用该运算符时的等价形式
a?.b // 等同于 a == null ? undefined : 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?.()也是这样
使用链判断运算符需要注意的事项:
(1)短路机制
?.运算符相当于一种短路机制,只要不满足条件,就不再往下执行
(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
,因此规定如果?.
后面紧跟一个十进制数字,那么?.
不再被看成是一个完整的运算符,而会按照三元运算符进行处理,也就是说,那个小数点会归属于后面的十进制数字,形成一个小数。