背景:同事和前段技术交流群里的网友们在同一天(21.06.28)都问出来一个问题。这个 变量?.**变量 是什么意思?我觉得这种巧合是一种上天的暗示,于是我决定去学习了一下。
可选链操作符( ?. )允许读取位于链接对象链深处的属性的值,而不必明确验证链中的每一个引用是否有效。 ?. 操作符的功能类似于 . 链式操作符,不同之处在于,在引用为空( null 或者 undefined )的情况下不会引起错误,该表达式短路返回值是 undefined 。与函数调用一起使用时,如果给定的函数不存在,则返回 undefined 。
当访问可能不存在的对象属性时,可选链操作符将会使表达式更短、更简明。在探索一个对象的内容时,如果不能明确哪些属性必定存在,可选链操作符也是很有帮助的。
项目中一个很常见的场景,从接口返回的数据,对象中的某哥属性可能不存在,即 undefined ;或者尝试获取DOM元素,该元素不存在,即 null 。下面是MDN官方的例子, cat 属性可能不存在:
// 可选链子 examplelet user = {name: 'syukinmei',// 该对象可能不包含 地址address 信息address: {province: '浙江'}};// 有省份名,log省份信息,否则四海为家if (user.address.province) {console.log(`${user.name} 住在 ${user.address.province}`);} else {console.log(`${user.name} 四海为家`);};// syukinmei 住在 浙江
我们会发现当 user 对象不含有 address 属性时就会报错。
这是因为if判断的时候我们使用了user深层次的属性,但是我们没有进行校验。
出于严谨性,我们就要判断user对象是存在的,并且user.address属性存在,并且user.address.province属性存在。
// 可选链子 examplelet user = {name: 'syukinmei',// 该对象可能不包含 地址address 信息address: {province: '浙江'}};// 有省份名,log省份信息,否则四海为家if (user && user.address && user.address.province) {console.log(`${user.name} 住在 ${user.address.province}`);} else {console.log(`${user.name} 四海为家`);};
当对象属性嵌套过深时,又不明确每个属性是否存在就可以通过可选链操作符来解决。
// 可选链子 examplelet user = {name: 'syukinmei',// 该对象可能不包含 地址address 信息address: {province: '浙江'}};// 有省份名,log省份信息,否则四海为家if (user?.address?.province) {console.log(`${user.name} 住在 ${user.address.province}`);} else {console.log(`${user.name} 四海为家`);};
如果属性都存在等价于
user.address.province
如果有属性不存在,后续更深层次的属性访问就不会报错
可选链操作符 ?. 有三种写法。
- obj?.prop // 对象属性是否存在
- obj?.[expr] // 同上
- func?.(…args) // 函数或对象方法是否存在
下面是 ?. 运算符常见形式,以及不使用该运算符时等等价形式。
a?.b// 等同于a == null ? undefined : a.ba?.[x]// 等同于a == null ? undefined : a[x]a?.b()// 等同于a == null ? undefined : a.b()a?.()// 等同于a == null ? undefined : a()
上面代码中,特别注意后两种形式,如果 a?.b() 和 a?.() 。如果 a?.b() 里面的 a.b 有值,但不是函数,不可调用,那么 a?.b() 是会报错的。 a?.() 也是如此,如果 a 不是 null 或者 undefined ,但也不是函数,那么 a?.() 会报错。
