Skip to content
On this page

描述

可选链运算符(optional chaining operator)即?.运算符。它可以获取对象的属性或调用方法,如果对象是 undefined 或 null,那么会返回 undefined,而不是直接抛错。

js
const adventurer = {
    name: 'Alice',
    cat: {
        name: 'Dinah',
    },
};

const dogName = adventurer.dog?.name;
console.log(dogName);
// expected output: undefined

console.log(adventurer.someNonExistentMethod?.());
// expected output: undefined

语法

obj.val?.prop
obj.val?.[expr]
obj.func?.(args)

注意这里方法和属性变量的使用。

简单来说:?.运算符很像.运算符,除了它不会报错。如果 ?.运算符前面的内容是 undefined 或 null,那么后面接的属性的获取或方法的调用,不会抛错,并且返回 undefined。

场景

获取属性

在之前,如果你想要探索对象是否存在属性,要这样:

js
const nestedProp = obj.first && obj.first.second;

这在 JS 中是惯用模式,但他有一下问题:

  1. 当嵌套层级很深时,代码冗余,过于啰嗦。
  2. obj.first的值是 Falsy 的时候,比如0,那么nestedProp的值就会变成0,而不是期望的 obj.first.second 的值或者undefined

但如果你使用?.运算符,那就很奈斯:

js
const nestedProp = obj.first?.second;

这样就解决了以上两个问题。

当然,你可能见过另一种解决方案,就是 lodash 的 get 方法

js
_.get(obj, 'first.second', undefined);

说实话,我更喜欢这个,因为它更强大,可惜的是,这并不是语言标准 😭

更准确来说,?.运算符其实是下面的语法糖:

js
const temp = obj.first;
const nestedProp =
    temp === null || temp === undefined ? undefined : temp.second;

?.运算符不可以用在没有声明的根对象上(如果用了会报错),但是可以用在已经声明的根对象的没有定义的属性上。

image-2022110480049953 PM

js
b?.a;
// Uncaught ReferenceError: b is not defined

let c = {};
c.d?.e;
// undefined

b 是未定义的对象,会报错,c 是定义了的,不会报错。

执行方法

js
const result = someInterface.customMethod?.();

常见于一些 api 可能未实现的情况。这里的调用会返回 undefined,而不是报错。

注意:如果customMethod是已经存在的属性,但不是方法,那即使这样写了也是会报错的。

用于表达式

js
const nestedProp = obj?.['prop' + 'Name'];

常用于数组:

js
function printMagicIndex(arr) {
    console.log(arr?.[42]);
}

printMagicIndex([0, 1, 2, 3, 4, 5]); // undefined
printMagicIndex(); // undefined; if not using ?., this would throw

?.运算符是不能在赋值左侧使用的,但.运算符是可以的

js
const object = {};
object?.property = 1; // SyntaxError: Invalid left-hand side in assignmen

短路操作

js
const potentiallyNullObj = null;
let x = 0;
const prop = potentiallyNullObj?.[x++];

console.log(x); // 0 as x was not incremented

如果?.前面的内容是 nullish,那么就不会执行后面的表达式,后续的属性访问也同样不会执行

js
const potentiallyNullObj = null;
const prop = potentiallyNullObj?.a.b;
// This does not throw, because evaluation has already stopped at
// the first optional chain

其实就是下面的代码:

js
const potentiallyNullObj = null;
const prop =
    potentiallyNullObj === null || potentiallyNullObj === undefined
        ? undefined
        : potentiallyNullObj.a.b;

但你如果用括号提升优先级,那么是有可能报错的:

js
const potentiallyNullObj = null;
const prop = (potentiallyNullObj?.a).b;

示例

用于可选的回调函数

js
// Code written without optional chaining
function doSomething(onContent, onError) {
    try {
        // Do something with the data
    } catch (err) {
        // Testing if onError really exists
        if (onError) {
            onError(err.message);
        }
    }
}

因为这里 onError 可能没有传值,所以要判断下,但如果用可选链,就比较简单:

js
// Using optional chaining with function calls
function doSomething(onContent, onError) {
    try {
        // Do something with the data
    } catch (err) {
        onError?.(err.message); // No exception if onError is undefined
    }
}

连续可选链

js
const customerCity = customer.details?.address?.city;

与空值合并运算符结合使用

js
function printCustomerCity(customer) {
    const customerCity = customer?.city ?? 'Unknown city';
    console.log(customerCity);
}

printCustomerCity({
    name: 'Nathan',
    city: 'Paris',
}); // "Paris"
printCustomerCity({
    name: 'Carl',
    details: { age: 82 },
}); // "Unknown city"

空值合并运算符

空值合并运算符 即??,它的作用是如果??左边是 nullish,那么返回右边,否则返回左边,它和||运算符很相似,||运算符左边是 Falsy 的话返回右边,否则返回左边。

参考:

Optional chaining (?.)

nullish

Falsy

Nullish coalescing operator (??)