背景:同事和前段技术交流群里的网友们在同一天(21.06.28)都问出来一个问题。这个 变量?.**变量 是什么意思?我觉得这种巧合是一种上天的暗示,于是我决定去学习了一下。
    image.png

    可选链操作符( ?. )允许读取位于链接对象链深处的属性的值,而不必明确验证链中的每一个引用是否有效。 ?. 操作符的功能类似于 . 链式操作符,不同之处在于,在引用为空( null 或者 undefined )的情况下不会引起错误,该表达式短路返回值是 undefined 。与函数调用一起使用时,如果给定的函数不存在,则返回 undefined

    当访问可能不存在的对象属性时,可选链操作符将会使表达式更短、更简明。在探索一个对象的内容时,如果不能明确哪些属性必定存在,可选链操作符也是很有帮助的。

    项目中一个很常见的场景,从接口返回的数据,对象中的某哥属性可能不存在,即 undefined ;或者尝试获取DOM元素,该元素不存在,即 null 。下面是MDN官方的例子, cat 属性可能不存在:

    1. // 可选链子 example
    2. let user = {
    3. name: 'syukinmei',
    4. // 该对象可能不包含 地址address 信息
    5. address: {
    6. province: '浙江'
    7. }
    8. };
    9. // 有省份名,log省份信息,否则四海为家
    10. if (user.address.province) {
    11. console.log(`${user.name} 住在 ${user.address.province}`);
    12. } else {
    13. console.log(`${user.name} 四海为家`);
    14. };
    15. // syukinmei 住在 浙江

    我们会发现当 user 对象不含有 address 属性时就会报错。
    image.png
    这是因为if判断的时候我们使用了user深层次的属性,但是我们没有进行校验。

    出于严谨性,我们就要判断user对象是存在的,并且user.address属性存在,并且user.address.province属性存在。

    1. // 可选链子 example
    2. let user = {
    3. name: 'syukinmei',
    4. // 该对象可能不包含 地址address 信息
    5. address: {
    6. province: '浙江'
    7. }
    8. };
    9. // 有省份名,log省份信息,否则四海为家
    10. if (user && user.address && user.address.province) {
    11. console.log(`${user.name} 住在 ${user.address.province}`);
    12. } else {
    13. console.log(`${user.name} 四海为家`);
    14. };

    当对象属性嵌套过深时,又不明确每个属性是否存在就可以通过可选链操作符来解决。

    1. // 可选链子 example
    2. let user = {
    3. name: 'syukinmei',
    4. // 该对象可能不包含 地址address 信息
    5. address: {
    6. province: '浙江'
    7. }
    8. };
    9. // 有省份名,log省份信息,否则四海为家
    10. if (user?.address?.province) {
    11. console.log(`${user.name} 住在 ${user.address.province}`);
    12. } else {
    13. console.log(`${user.name} 四海为家`);
    14. };

    如果属性都存在等价于

    1. user.address.province

    如果有属性不存在,后续更深层次的属性访问就不会报错

    可选链操作符 ?. 有三种写法。

    • obj?.prop // 对象属性是否存在
      - obj?.[expr] // 同上
      - func?.(…args) // 函数或对象方法是否存在

    下面是 ?. 运算符常见形式,以及不使用该运算符时等等价形式。

    1. a?.b
    2. // 等同于
    3. a == null undefined : a.b
    4. a?.[x]
    5. // 等同于
    6. a == null ? undefined : a[x]
    7. a?.b()
    8. // 等同于
    9. a == null ? undefined : a.b()
    10. a?.()
    11. // 等同于
    12. a == null ? undefined : a()

    上面代码中,特别注意后两种形式,如果 a?.b() 和 a?.() 。如果 a?.b() 里面的 a.b 有值,但不是函数,不可调用,那么 a?.b() 是会报错的。 a?.() 也是如此,如果 a 不是 null 或者 undefined ,但也不是函数,那么 a?.() 会报错。