由于JS的动态特性,对象可以具有多层不同的嵌套对象结构。
通常,当咱们处理以下这些对象时:
获取远程JSON数据
使用配置对象
有可选属性
虽然JS为对象支持不同层次数据结构,但是在访问此类对象的属性时,复杂性也随着增加。
bigObject可以在运行时拥有不同的属性集
// 嵌套版本
constbigObject = {
// ...
prop1: {
//...
prop2: {
// ...
value:'Some value'
}
}
};
// 简单版本
constbigObject = {
// ...
prop1: {
// Nothing here
}
};
因此,必须手动检查属性是否存在
if(bigObject &&
bigObject.prop1 !=null&&
bigObject.prop1.prop2 !=null) {
letresult = bigObject.prop1.prop2.value;
}
这样写太过冗长了,最好避免写它。
咱们来看看可选链如何解决这个问题,以减少冗余的代码。
2. 易于深入访问属性
设计一个保存电影信息的对象。该对象包含必填title属性,以及可选的director和actors。
movieSmall对象仅包含title,而movieFull包含完整的属性集:
constmovieSmall = {
title:'Heat'
};
constmovieFull = {
title:'Blade Runner',
director: { name:'Ridley Scott'},
actors: [{ name:'Harrison Ford'}, { name:'Rutger Hauer'}]
};
写一个获取director的函数。请记住,director 可能不存在。
functiongetDirector(movie){
if(movie.director !=null) {
returnmovie.director.name;
}
}
getDirector(movieSmall);// => undefined
getDirector(movieFull);// => 'Ridley Scott'
if(movie.director){...}条件用于验证是否定义了director属性。如果没有这个预防措施,在访问movieSmall对象的director时,JS会抛出TypeError: Cannot read property 'name' of undefined。
这种场景最适合使用可选链的功能了,如下所示,代码将简洁很多。
functiongetDirector(movie){
returnmovie.director?.name;
}
getDirector(movieSmall);// => undefined
getDirector(movieFull);// => 'Ridley Scott'
在movie.director?.name表达式中可以找到?:可选的链接操作符。
在movieSmall中,没有director属性。因此,movie.director?.name的的结果为undefined。可选链运算符可防止抛出 TypeError: Cannot read property 'name' of undefined。
简单地说,代码片段:
letname= movie.director?.name;
等价于
letname;
if(movie.director !=null) {
name = movie.director.name;
}
?.通过减少两行代码简化getDirector()函数,这就是为什么我喜欢可选链的原因。
2.1 数组项
可选的链功能可以做得更多。可以自由地在同一个表达式中使用多个可选的链接操作符,甚至可以使用它安全地访问数组项。
下一个任务是编写一个函数,返回电影的actors中的name。
在movie对象中,actors数组可以是空的,甚至是缺失的,因此必须添加额外的条件来判空。
functiongetLeadingActor(movie){
if(movie.actors && movie.actors.length >0) {
returnmovie.actors[0].name;
}
}
getLeadingActor(movieSmall);// => undefined
getLeadingActor(movieFull);// => 'Harrison Ford'
if (movie.actors && movies.actors.length > 0) {...}条件主要判断movie包含actors属性,并且此属性至少有一个actor。
使用可选链接,同样代码也简洁了很了,如下:
functiongetLeadingActor(movie){
returnmovie.actors?.[0]?.name;
}
getLeadingActor(movieSmall);// => undefined
getLeadingActor(movieFull);// => 'Harrison Ford'
actors?. 确保actors属性存在, [0]?.确保列表中存在第一个actor。
3.双问号操作符
一个名为nullish coalescing operator(双问号操作符)的新提案用来处理undefined或null,将它们默认为特定值。
表达式变量?? 如果变量undefined或为null,则默认值为指定的值。
constnoValue = undefined;
constvalue='Hello';
noValue ??'Nothing';// => 'Nothing'
value??'Nothing';// => 'Hello'
接着使用??来优化一下 getLeading()函数,当movie对象中没有actor时返回“Unknown actor”
functiongetLeadingActor(movie){
returnmovie.actors?.[0]?.name ??'Unknown actor';
}
getLeadingActor(movieSmall);// => 'Unknown actor'
getLeadingActor(movieFull);// => 'Harrison Ford'
4. 可选链的三种形式
咱们可以使用以下3种形式的可选链。
第一种:object?.property 用于访问静态属性:
constobject=null;
object?.property;// => undefined
第二种:object?.[expression] 用于访问动态属性或数组项:
// 其一
constobject= null;
constname='property';
object?.[name];// => undefined
// 其二
constarray= null;
array?.[0];// => undefined
第三种:object?.([arg1, [arg2, ...]]) 执行一个对象方法
constobject=null;
object?.method('Some value');// => undefined
将这三种组合起来创建一个可选链:
constvalue=object.maybeUndefinedProp?.maybeNull()?.[propName];
5.短路:遇到 `null/undefined` 停止
可选链接运算符的有趣之处在于,只要在左侧leftHandSide?.rightHandSide遇到无效值,右侧访问就会停止,这称为短路。
看看例子:
constnothing = null;
letindex=0;
nothing?.[index++];// => undefined
index;// => 0
6. 何时使用可选链
不要急于使用可选的链操作符来访问任何类型的属性:这会导致错误的使用。
6.1访问潜在无效的属性
?.一般使用在可能为空的属性:maybeNullish?.prop。在确定属性不为空的情况下,使用属性访问器:.property或[propExpression]。
// 好
functionlogMovie(movie){
console.log(movie.director?.name);
console.log(movie.title);
}
// 不好
functionlogMovie(movie){
// director needs optional chaining
console.log(movie.director.name);
// movie doesn't need optional chaining
console.log(movie?.title);
}
6.2 通常有更好的选择
以下函数hasPadding()接收可选padding属性的样式对象。padding具有left,top,right,bottom可选属性。
尝试使用可选的链操作符:
functionhasPadding({ padding }){
consttop = padding?.top ??0;
constright = padding?.right ??0;
constbottom = padding?.bottom ??0;
constleft = padding?.left ??0;
returnleft + top + right + bottom !==0;
}
hasPadding({color:'black'});// => false
hasPadding({padding: {left:0} });// => false
hasPadding({padding: {right:10}});// => true
虽然函数正确地确定了元素是否有padding,但是对于每个属性使用可选的链有点过于麻烦了。
更好的方法是使用对象扩展操作符将padding对象默认为零值
functionhasPadding({ padding }){
constp = {
top:0,
right:0,
bottom:0,
left:0,
...padding
};
returnp.top + p.left + p.right + p.bottom !==0;
}
hasPadding({color:'black'});// => false
hasPadding({padding: {left:0} });// => false
hasPadding({padding: {right:10}});// => true
这个就比可选链来的更简洁。
代码部署后可能存在的BUG没法实时知道,事后为了解决这些BUG,花了大量的时间进行log 调试,这边顺便给大家推荐一个好用的BUG监控工具 Fundebug。
原文:https://dmitripavlutin.com/javascript-optional-chaining/
网友评论