可选链操作符 Optional chaining
可选链操作符( ?. )
允许读取位于连接对象链深处的属性的值,而不必明确验证链中的每个引用是否有效。?.
操作符的功能类似于.
链式操作符,不同之处在于,在引用为空(nullish
) (null
或者undefined
) 的情况下不会引起错误,该表达式短路返回值是undefined
。与函数调用一起使用时,如果给定的函数不存在,则返回undefined
。
当尝试访问可能不存在的对象属性时,可选链操作符将会使表达式更短、更简明。在探索一个对象的内容时,如果不能确定哪些属性必定存在,可选链操作符也是很有帮助的。
const adventurer={
name:'Alice',
cat:{
name:'Dinah'}};// 直接操作未知的变量和方法有可能报错
console.log(adventurer.aa.bb)// Uncaught TypeError: Cannot read property 'bb' of undefined
console.log(adventurer.aa())// Uncaught TypeError: adventurer.aa is not a function// 使用可选链操作符可避免报错const dogName= adventurer.dog?.name;
console.log(dogName);// undefined
console.log(adventurer.someNonExistentMethod?.());// undefined
语法:
obj?.prop// 对象
obj?.[expr]// 对象
arr?.[index]// 数组
func?.(args)// 函数
通过连接的对象的引用或函数可能是undefined
或null
时,可选链操作符提供了一种方法来简化被连接对象的值访问。
比如,思考一个存在嵌套结构的对象 obj。不使用可选链的话,查找一个深度嵌套的子属性时,需要验证之间的引用,例如:
// 以前的写法let nestedProp= obj.first&& obj.first.second;// 可选链操作符写法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);
可选链与函数使用
let result= someInterface.customMethod?.();
函数使用可选链操作符场景:
functiondoSomething(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];
空值合并运算符(Nullish coalescing Operator)
空值合并操作符(??)
是一个逻辑操作符,当左侧的操作数为null
或者undefined
时,返回其右侧操作数,否则返回左侧操作数。
与逻辑或操作符(||)
不同,逻辑或操作符会在左侧操作数为假值时返回右侧操作数。也就是说,如果使用 || 来为某些变量设置默认值,可能会遇到意料之外的行为。比如为假值(例如,’’ 或 0)时。见下面的例子。
const foo=null??'default string';
console.log(foo);// "default string"const baz=0??42;
console.log(baz);// 0
使用空值合并操作符
console.log(null??"defaultValue1")// "defaultValue1"
console.log(undefined??"defaultValue2")// "defaultValue2"
console.log(""??"defaultValue3")// ""
console.log(0??"defaultValue4")// 0
console.log(40??"defaultValue5")// 40
为变量赋默认值
以前,如果想为一个变量赋默认值,通常的做法是使用逻辑或操作符(||):
let foo;// foo is never assigned any value so it is still undefinedlet someDummyText= foo||'Hello!';
然而,由于||
是一个布尔逻辑运算符,左侧的操作数会被强制转换成布尔值用于求值。任何假值(0
,''
,NaN
,null
,undefined
)都不会被返回。这导致如果你使用0
,''
或NaN
作为有效值,就会出现不可预料的后果。
let count=0;let text="";let qty= count||42;let message= text||"hi!";
console.log(qty);// 42,而不是 0
console.log(message);// "hi!",而不是 ""
空值合并操作符可以避免这种陷阱,其只在第一个操作数为null
或undefined
时(而不是其它假值)返回第二个操作数:
let myText='';let notFalsyText= myText||'Hello world';
console.log(notFalsyText);// Hello worldlet preservingFalsy= myText??'Hi neighborhood';
console.log(preservingFalsy);// ''
短路
与OR
和AND
逻辑操作符相似,当左表达式不为null
或undefined
时,不会对右表达式进行求值。
functionA(){ console.log('函数 A 被调用了');return undefined;}functionB(){ console.log('函数 B 被调用了');returnfalse;}functionC(){ console.log('函数 C 被调用了');return"foo";}
console.log(A()??C());// 依次打印 "函数 A 被调用了"、"函数 C 被调用了"、"foo"// A() 返回了 undefined,所以操作符两边的表达式都被执行了
console.log(B()??C());// 依次打印 "函数 B 被调用了"、"false"// B() 返回了 false(既不是 null 也不是 undefined)// 所以右侧表达式没有被执行
不能直接与AND或OR操作符共用
null|| undefined??"foo";// 抛出 SyntaxErrortrue|| undefined??"foo";// 抛出 SyntaxError
但是加了括号来表明运算优先级,没有问题:
(null|| undefined)??"foo";// 返回 "foo"
与可选链操作符(?.)的关系
let customer={
name:"Carl",
details:{ age:82}};let customerCity= customer?.city??"暗之城";
console.log(customerCity);// “暗之城”