可选链操作符

可选链操作符 ?. 可以按照操作符之前的属性是否有效,链式读取对象的属性或者使整个对象链返回 undefined?. 运算符的作用与 . 运算符类似,不同之处在于,如果对象链上的引用是 nullish (null 或者 undefined),. 操作符会抛出一个错误,而 ?. 操作符则会按照短路计算的方式进行处理,返回 undefined可选链操作符也可用于函数调用,如果操作符前的函数不存在,也将会返回 undefined

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

语法

obj?.prop
obj?.[expr]
arr?.[index]
func?.(args)

描述

通过连接的对象的引用或函数可能是 undefinednull 时,可选链操作符提供了一种方法来简化被连接对象的值访问。

比如,思考一个存在嵌套结构的对象 obj。不使用可选链的话,查找一个深度嵌套的子属性时,需要验证之间的引用,例如:

let nestedProp = obj.first && obj.first.second;

为了避免报错,在访问obj.first.second之前,要保证 obj.first 的值既不是 null,也不是 undefined。如果只是直接访问 obj.first.second,而不对 obj.first 进行校验,则有可能抛出错误。

有了可选链操作符(?.),在访问 obj.first.second 之前,不再需要明确地校验 obj.first 的状态,再并用短路计算获取最终结果:

let nestedProp = obj.first?.second;

通过使用 ?. 操作符取代 . 操作符,JavaScript 会在尝试访问 obj.first.second 之前,先隐式地检查并确定 obj.first 既不是 null 也不是 undefined。如果obj.first null 或者 undefined,表达式将会短路计算直接返回 undefined

这等价于以下表达式,但实际上没有创建临时变量:

let temp = obj.first;
let nestedProp = ((temp === null || temp === undefined) ? undefined : temp.second);

可选链与函数调用

当尝试调用一个可能不存在的方法时也可以使用可选链。这将是很有帮助的,比如,当使用一个API的方法可能不可用时,要么因为实现的版本问题要么因为当前用户的设备不支持该功能。

函数调用时如果被调用的方法不存在,使用可选链可以使表达式自动返回undefined而不是抛出一个异常。

let result = someInterface.customMethod?.();

注意: 如果存在一个属性名且不是函数, 使用 ?. 仍然会产生一个 TypeError 异常 (x.y is not a function).

处理可选的回调函数或者事件处理器

如果使用解构赋值来解构的一个对象的回调函数或 fetch 方法,你可能得到不能当做函数直接调用的不存在的值,除非你已经校验了他们的存在性。使用?.的你可以忽略这些额外的校验:

//  ES2019的写法
function doSomething(onContent, onError) {
  try {
    // ... do something with the data 
  }
  catch (err) {
    if (onError) { // 校验onError是否真的存在
      onError(err.message);
    }
  }
}
// 使用可选链进行函数调用
function doSomething(onContent, onError) {
  try {
   // ... do something with the data
  }
  catch (err) {
    onError?.(err.message); // 如果onError是undefined也不会有异常
  }
}

可选链和表达式

当使用方括号与属性名的形式来访问属性时,你也可以使用可选链操作符:

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

可选链不能用于赋值

let object = {};
object?.property = 1; // Uncaught SyntaxError: Invalid left-hand side in assignment

可选链访问数组元素

let arrayItem = arr?.[42];

例子

基本例子

如下的例子在一个不含 bar 成员的 Map 中查找 bar 成员的 name 属性,因此结果是 undefined

let myMap = new Map();
myMap.set("foo", {name: "baz", desc: "inga"});

let nameBar = myMap.get("bar")?.name;

短路计算

当在表达式中使用可选链时,如果左操作数是 nullundefined,表达式将不会被计算,例如:

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

console.log(x); // x 将不会被递增,依旧输出 0

连用可选链操作符

可以连续使用可选链读取多层嵌套结构:

let customer = {
  name: "Carl",
  details: {
    age: 82,
    location: "Paradise Falls" // details 的 address 属性未有定义
  }
};
let customerCity = customer.details?.address?.city;

// … 可选链也可以和函数调用一起使用
let duration = vacations.trip?.getTime?.();

使用空值合并操作符

空值合并操作符可以在使用可选链时设置一个默认值:

let customer = {
  name: "Carl",
  details: { age: 82 }
};
let customerCity = customer?.city ?? "暗之城";
console.log(customerCity); // “暗之城”

规范

规范 状态 备注
ECMAScript (ECMA-262)
optional expression
Stage 4

浏览器兼容性

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
Optional chaining operator (?.)Chrome Full support 80
Full support 80
Full support 79
Disabled
Disabled From version 79: this feature is behind the Experimental JavaScript preference (needs to be set to true). To change preferences in Chrome, visit chrome://flags.
Edge Full support 80
Full support 80
Full support 79
Disabled
Disabled From version 79: this feature is behind the Experimental JavaScript preference (needs to be set to true).
Firefox Full support 74IE No support NoOpera Full support 67
Full support 67
Full support 66
Disabled
Disabled From version 66: this feature is behind the Experimental JavaScript preference (needs to be set to true).
Safari Full support 13.1WebView Android Full support 80Chrome Android Full support 80
Full support 80
Full support 79
Disabled
Disabled From version 79: this feature is behind the Experimental JavaScript preference (needs to be set to true). To change preferences in Chrome, visit chrome://flags.
Firefox Android No support NoOpera Android No support NoSafari iOS Full support 13.4Samsung Internet Android No support Nonodejs Full support 14.0.0

Legend

Full support  
Full support
No support  
No support
User must explicitly enable this feature.
User must explicitly enable this feature.

Implementation Progress

The following table provides a daily implementation status for this feature, because this feature has not yet reached cross-browser stability. The data is generated by running the relevant feature tests in Test262, the standard test suite of JavaScript, in the nightly build, or latest release of each browser's JavaScript engine.

参见