可选链

这是一个实验中的功能
此功能某些浏览器尚在开发中,请参考浏览器兼容性表格以得到在不同浏览器中适合使用的前缀。由于该功能对应的标准文档可能被重新修订,所以在未来版本的浏览器中该功能的语法和行为可能随之改变。

可选链操作符?.能够去读取一个被连接对象的深层次的属性的值而无需明确校验链条上每一个引用的有效性。?.运算符功能类似于.运算符,不同之处在于如果链条上的一个引用是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.firstt是非null且非undefined。如果obj.firstnullundefined,表达式将会短路计算直接返回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).

处理可选的callbacks或事件handlers

如果你使用解构赋值来解构的一个对象的callbacks或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'];

例子

基本例子

如下的例子在一个不存在bar成员的map中查找barname的值,因此结果是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); // 作为0的x将不会被递增,依旧输出0

叠加可选链操作符

嵌套结构,可以多次使用可选链:

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

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

使用 nullish 合并运算符

Nullish 合并运算符可以在使用可选链时设置一个默认值:

let customer = {
  name: "Carl",
  details: { age: 82 }
};
let customerCity = customer?.city ?? "Unknown city";
console.log(customerCity); // Unknown city

说明

说明 状态 建议
Proposal for the "optional chaining" operator Stage 4

Browser compatibility

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 No support NoFirefox Full support 74IE No support NoOpera Full support 67
Full support 67
Full support 65
Disabled
Disabled From version 65: this feature is behind the Experimental JavaScript preference (needs to be set to true).
Safari No support NoWebView 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 No support NoSamsung Internet Android No support Nonodejs No support No

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.

See also

See also