项目中一个很常见的场景,从接口返回的数据,对象中的某个属性可能不存在,即 undefined
;或者尝试获取 DOM 元素,该元素不存在,即 null
。下面是 MDN 官方的例子,cat
属性可能不存在:
const adventurer = {
name: 'Alice',
cat: {
name: 'Dinah'
}
};
如果想要访问 cat
的 name
属性,像下面这样可能会报错:
const name = adventurer.cat.name
Uncaught TypeError: Cannot read property ‘name’ of undefined
如果 cat
属性不存在,那么 adventurer.cat
的值就是 undefined
。在 undefined
上访问 name
就会出现上面的报错信息。一旦报错,这段代码的执行就中断了,进而影响其他代码的执行甚至阻塞页面加载。
使用 AND 运算符
为了避免出现报错,项目中常见的作法是这样的:
const name = adventurer && adventurer.cat && adventurer.cat.name
AND运算符
&&
当左边的表达式为true
,不管右边表达真假,都返回右边的表达式;当左边表达式为false
直接返回左边的表达式
上面的代码中,查找一个深度嵌套的子属性时,需要验证之间的引用。这样当 cat
属性不存在时,提前进行了返回,从而避免报错。
这样做很明显有个缺点,当访问的属性嵌套了很多层,校验的语句会变得很长,影响代码可读性。
使用可选链操作符
可选链操作符(?.),在访问子属性之前,不再需要明确地校验当前属性的状态,再并用短路计算获取最终结果:
const name = adventurer?.cat?.name
通过使用 ?.
操作符取代 .
操作符,JavaScript 会在尝试访问 adventurer.cat.name
之前,先隐式地检查并确定 adventurer.cat
既不是 null
也不是 undefined
。如果 adventurer.cat
是 null
或者 undefined
,表达式将会短路计算直接返回 undefined
。
这等价于以下表达式,但实际上没有创建临时变量:
let temp = adventurer.cat;
let name = ((temp === null || temp === undefined) ? undefined : temp.name);
可选链常用语法如下:
obj?.prop
obj?.[expr]
arr?.[index]
func?.(args)
函数调用
函数调用时如果被调用的方法不存在,使用可选链可以使表达式自动返回undefined而不是抛出一个异常:
let result = someInterface.customMethod?.();
对象属性
可选链用于访问对象属性:
let nestedProp = obj?.first;
对象属性支持表达式:
let nestedProp = obj?.['prop' + 'Name'];
可选链不能用于赋值
注意:可选链只能用于访问属性,不能用来赋值。
let object = {};
object?.property = 1;
上面这段代码执行会报错:
Uncaught SyntaxError: Invalid left-hand side in assignment
数组元素
let arrayItem = arr?.[42];
短路计算
当在表达式中使用可选链时,如果左操作数是 null 或 undefined,表达式将不会被计算:
let potentiallyNullObj = null;
let x = 0;
let prop = potentiallyNullObj?.[x++];
// 相当于下面代码
// let prop = potentiallyNullObj && x++;
console.log(x); // x 将不会被递增,依旧输出 0
连用可选链操作符
可以连续使用可选链读取多层嵌套结构:
let customerCity = customer.details?.address?.city;
let duration = vacations.trip?.getTime?.();
参考:
可选链操作符 - MDN