描述
可选链运算符(optional chaining operator)即?.
运算符。它可以获取对象的属性或调用方法,如果对象是 undefined 或 null,那么会返回 undefined,而不是直接抛错。
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。
场景
获取属性
在之前,如果你想要探索对象是否存在属性,要这样:
const nestedProp = obj.first && obj.first.second;
这在 JS 中是惯用模式,但他有一下问题:
- 当嵌套层级很深时,代码冗余,过于啰嗦。
- 当
obj.first
的值是 Falsy 的时候,比如0
,那么nestedProp
的值就会变成0
,而不是期望的obj.first.second
的值或者undefined
但如果你使用?.
运算符,那就很奈斯:
const nestedProp = obj.first?.second;
这样就解决了以上两个问题。
当然,你可能见过另一种解决方案,就是 lodash 的 get 方法
js_.get(obj, 'first.second', undefined);
说实话,我更喜欢这个,因为它更强大,可惜的是,这并不是语言标准 😭
更准确来说,?.
运算符其实是下面的语法糖:
const temp = obj.first;
const nestedProp =
temp === null || temp === undefined ? undefined : temp.second;
?.
运算符不可以用在没有声明的根对象上(如果用了会报错),但是可以用在已经声明的根对象的没有定义的属性上。
b?.a;
// Uncaught ReferenceError: b is not defined
let c = {};
c.d?.e;
// undefined
b 是未定义的对象,会报错,c 是定义了的,不会报错。
执行方法
const result = someInterface.customMethod?.();
常见于一些 api 可能未实现的情况。这里的调用会返回 undefined,而不是报错。
注意:如果customMethod
是已经存在的属性,但不是方法,那即使这样写了也是会报错的。
用于表达式
const nestedProp = obj?.['prop' + 'Name'];
常用于数组:
function printMagicIndex(arr) {
console.log(arr?.[42]);
}
printMagicIndex([0, 1, 2, 3, 4, 5]); // undefined
printMagicIndex(); // undefined; if not using ?., this would throw
?.
运算符是不能在赋值左侧使用的,但.
运算符是可以的
const object = {};
object?.property = 1; // SyntaxError: Invalid left-hand side in assignmen
短路操作
const potentiallyNullObj = null;
let x = 0;
const prop = potentiallyNullObj?.[x++];
console.log(x); // 0 as x was not incremented
如果?.
前面的内容是 nullish,那么就不会执行后面的表达式,后续的属性访问也同样不会执行
const potentiallyNullObj = null;
const prop = potentiallyNullObj?.a.b;
// This does not throw, because evaluation has already stopped at
// the first optional chain
其实就是下面的代码:
const potentiallyNullObj = null;
const prop =
potentiallyNullObj === null || potentiallyNullObj === undefined
? undefined
: potentiallyNullObj.a.b;
但你如果用括号提升优先级,那么是有可能报错的:
const potentiallyNullObj = null;
const prop = (potentiallyNullObj?.a).b;
示例
用于可选的回调函数
// 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 可能没有传值,所以要判断下,但如果用可选链,就比较简单:
// 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
}
}
连续可选链
const customerCity = customer.details?.address?.city;
与空值合并运算符结合使用
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 的话返回右边,否则返回左边。
参考: