项目中一个很常见的场景,从接口返回的数据,对象中的某个属性可能不存在,即 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 arr=[23,45,4,56]
console.log(arr?.[1])//45
console.log(arr?.[4])//undefined
短路计算
当在表达式中使用可选链时,如果左操作数是 null 或 undefined,表达式将不会被计算:
let potentiallyNullObj = null;
let x = 0;
let prop = potentiallyNullObj?.[x++];
// 相当于下面代码
// let prop = potentiallyNullObj && x++;
console.log(x); //0 (x 将不会被递增,依旧输出 0)
console.log(prop); //undefined
console.log(potentiallyNullObj ); //null
注意:prop没有被赋值
连用可选链操作符
可以连续使用可选链读取多层嵌套结构:
let customerCity = customer.details?.address?.city;
let duration = vacations.trip?.getTime?.();