可选链"?."
可选链?.
是JavaScript
新添加的一个语法,是一种访问嵌套对象属性的安全方式,可以避免出现属性不存在错误。
前言
在使用对象编程的时候,我们经常会遇到属性不存在的问题。例如我们定义一个空的对象,然后访问这个对象的属性:
let people = {
};//定义一个空的对象
console.log(people.name);//name 属性不存在 undefine
毫无疑问,以上代码将输出undefined
。问题在于,如果我们想通过name
属性,访问surname
属性:
let people={
};
console.log(people.name.surname);//访问名称中的姓
此时,就会发生意料之中的错误:
> Error: undefined is not an object (evaluating 'people.name.surname')
出现这种错误的原因在于,people.name
本身就是一个undefined
的值,而以上代码尝试获取一个undefined
对象的属性,显然是不合适的。
在实际的编程场景中,我们经常会遇到对象相会嵌套的情况,例如:China.Beijing.Fengtai.Shilipu...
,在这种多层级的嵌套访问情况下,如果其中任意一环的值是null
或者undefined
,就会引发上述异常。
而我们希望的结果是,不论是末级属性还是中间对象不存在,都统一返回undefined
。实际上,使用之前的知识同样可以做到这点,不过稍显繁琐。
例如,我们可以使用三目运算符:
let people = {
};
let surname = people.name?people.name.surname?people.name.surname.firstCharacter:null:null;
看起来还不错,但是试想一下,如果存在四级甚至多层次的属性,需要嵌套多少层三目运算符!这太可怕了。
还有一种稍微优雅一点的方法&&
:
let people = {
};
let surname = people.name && people.name.surname && people.name.surname.firstCharacter;
利用&&
链返回第一个假值的特性,可以相当优雅的实现多层级对象属性访问。但是,这并不是最优雅的方式,people.name
已经被我们写了好多遍,如果层级更多,我们还要写更多遍。
这就是?.
语法被加入的原因。
可选链使用
可选链?.
的作用是,如果可选链?.
前面的对象是null
或者undefined
,那么他就会停止执行并返回 undefined
。
本文设定:如果一个值是 null 或者 undefined,那么他就是"不存在的",否则就是"存在的"。
举个例子:
obj?.prop;
以上代码结果的计算方式为:
- 如果 obj 存在,那么返回
obj.prop
; - 如果 obj 不存在,直接返回
undefined
;
此时,如果我们访问深层次的属性,就可以采用如下格式:
let people = {
};
let lastCharacter = people?.name?.surname?.lastCharacter;
lastCharacter
最终的结果就是undefined
。
这样,我们就以最优雅的方式实现了可能存在错误的访问方式,保证程序最坏的结果就是返回 undefined
。
我们还可以对值为null
的对象使用,甚至直接对null
使用null?.name
同样不会出现程序异常:
let user = null;
console.log(user?.name);
console.log(null?.name);
代码执行结果为:
> undefined
> undefined
说明:
可选链?.
是对其前面的对象生效的,例如:user.name?.surname.firstCharacter
中,只对name
属性负责。
如果surname
、user
不存在,那么同样会报错。
过度使用可选链不可取
有些童鞋肯定就会认为?.
比.
要好用,毕竟不会那么容易报错了,不如使用?.
完全代替.
访问属性吧?
这种思想是错误的,在访问多层级嵌套属性时,使用.
可以及时的发现程序中的错误,例如我么在开发过程中,访问user.name.surname
时发生了错误,那么我们就可以早早的发现这个错误,然后评估这个错误的合理性,从而改进我们的代码。
然是,如果大量使用、甚至全盘使用?.
就会抑制错误的爆发,消除一些本不该消除的错误,导致程序出现莫名其妙的错误,而且很难通过日志信息定位错误的位置。
我们要改变"报错是邪恶的"这种观念,实际上,报错可以告诉我们错误的位置和错误的信息,帮助我们编写更加优质的代码。
可选链同样会出错,例如我们对一个没有声明的变量使用,例如
obj?.name
中的obj
还没有声明,那就会报错。
可选链的短路效应及其用法
?.
对左侧的值负责,如果左侧的值不存在,立即停止执行,右边的内容不会执行。这就和||
、&&
的短路效应相似,我们可以利用这种特性调用方法。
举个例子:
let user = {
hello:function(){
console.log('hello');}
}
user?.hello();//执行 hello 函数,输出hello
user = null;
user?.hello();//不会执行 hello 函数
可选链的变体
可选链?.
不是运算符,而是一个长的奇怪的语法结构,他可以和其他函数和方括号一起使用。
- 使用可选链调用一个可能不存在的函数
?.()
let user = {
hello:function(){
console.log('hello');}
};
user.hello?.();//执行 hello 函数
let dog = {
};
dog.hello?.();//什么都没有发生
以上代码中,?.
会判断前面的属性是否存在,如果存在,则使用后面的小括号调用函数,如果不存在,立即停止执行,什么都没有发生。如果我们使用console.log(dog.hello?.());
输出最后一行代码,会发现输出了一个undefined
。
?.[]
访问属性
在访问对象属性时,如果不使用.
,我们还可以选择使用[]
,如果配合可选链,就能安全的访问一个可能不存在的属性:
let user = {
name: 'xiaoming', };
let dog = {
};
console.log(user?.['name']);//xiaoming
console.log(dog?.['name']);//undefined
虽然看起来很奇怪,但是仍然可以正确的执行。
- 删除可能不存在的属性
我们还可以结合delete
删除一个可能不存在的属性:
delete user?.name;
如果user.name
属性不存在,同样不会出错。
总结
可选链的三种使用方式:
obj?.prop?.prop
可以安全的访问可能不存在的属性,最坏的结果就是返回undefined
;obj.method?.()
可以安全的调用一个可能不存在的方法;obj?.[prop]
安全的访问一个可能不存在的属性;obj?.mentod()
安全的访问一个可能不存在对象的方法;