背景:同事和前段技术交流群里的网友们在同一天(21.06.28)都问出来一个问题。这个 变量?.**变量 是什么意思?我觉得这种巧合是一种上天的暗示,于是我决定去学习了一下。
可选链操作符( ?.
)允许读取位于链接对象链深处的属性的值,而不必明确验证链中的每一个引用是否有效。 ?.
操作符的功能类似于 .
链式操作符,不同之处在于,在引用为空( null 或者 undefined )的情况下不会引起错误,该表达式短路返回值是 undefined
。与函数调用一起使用时,如果给定的函数不存在,则返回 undefined
。
当访问可能不存在的对象属性时,可选链操作符将会使表达式更短、更简明。在探索一个对象的内容时,如果不能明确哪些属性必定存在,可选链操作符也是很有帮助的。
项目中一个很常见的场景,从接口返回的数据,对象中的某哥属性可能不存在,即 undefined ;或者尝试获取DOM元素,该元素不存在,即 null 。下面是MDN官方的例子, cat 属性可能不存在:
// 可选链子 example
let 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属性存在。
// 可选链子 example
let 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} 四海为家`);
};
当对象属性嵌套过深时,又不明确每个属性是否存在就可以通过可选链操作符来解决。
// 可选链子 example
let 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.b
a?.[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?.() 会报错。