JS的一些特性极大地改变了咱们的编码方式。从ES6年开始,对咱们代码影响最大的特性的解 、箭头函数、类和模块系统。
到2019年8月,一个新的可选链提案已经进入第三阶段,这是一个很好的改进。可选链接改变了从深层对象结构访问属性的方式。
来看看这是又是什么骚操作。
这个礼拜《大迁世界》有抽奖活动,奖品:专栏 《左耳听风》 x3, 技术书 x5,欢迎关注回复:抽奖
问题
由于JS的动态特性,对象可以具有多层不同的嵌套对象结构。
通常,当咱们处理以下这些对象时:
获取远程JSON数据
使用配置对象
有可选属性
虽然JS为对象支持不同层次数据结构,但是在访问此类对象的属性时,复杂性也随着增加。
bigObject
可以在运行时拥有不同的属性集
// 嵌套版本 const bigObject = { // ... prop1: { //... prop2: { // ... value: 'Some value' } } }; // 简单版本 const bigObject = { // ... prop1: { // Nothing here } };
因此,必须手动检查属性是否存在
if (bigObject && bigObject.prop1 != null && bigObject.prop1.prop2 != null) { let result = bigObject.prop1.prop2.value; }
这样写太过冗长了,最好避免写它。
咱们来看看可选链如何解决这个问题,以减少冗余的代码。
2. 易于深入访问属性
设计一个保存电影信息的对象。 该对象包含必填title
属性,以及可选的director
和actors
。
movieSmall
对象仅包含title
,而movieFull
包含完整的属性集:
const movieSmall = { title: 'Heat' }; const movieFull = { title: 'Blade Runner', director: { name: 'Ridley Scott' }, actors: [{ name: 'Harrison Ford' }, { name: 'Rutger Hauer' }] };
写一个获取director
的函数。 请记住,director
可能不存在。
function getDirector(movie) { if (movie.director != null) { return movie.director.name; } } getDirector(movieSmall); // => undefined getDirector(movieFull); // => 'Ridley Scott'
if(movie.director){...}
条件用于验证是否定义了director
属性。 如果没有这个预防措施,在访问movieSmall
对象的director
时,JS会抛出TypeError: Cannot read property 'name' of undefined
。
这种场景最适合使用可选链的功能了,如下所示,代码将简洁很多。
function getDirector(movie) { return movie.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
。
简单地说,代码片段:
let name = movie.director?.name;
等价于
let name; if (movie.director != null) { name = movie.director.name; }
?.
通过减少两行代码简化getDirector()
函数,这就是为什么我喜欢可选链的原因。
2.1 数组项
可选的链功能可以做得更多。可以自由地在同一个表达式中使用多个可选的链接操作符,甚至可以使用它安全地访问数组项。
下一个任务是编写一个函数,返回电影的actors
中的name
。
在movie
对象中,actors
数组可以是空的,甚至是缺失的,因此必须添加额外的条件来判空。
function getLeadingActor(movie) { if (movie.actors && movie.actors.length > 0) { return movie.actors[0].name; } } getLeadingActor(movieSmall); // => undefined getLeadingActor(movieFull); // => 'Harrison Ford'
if (movie.actors && movies.actors.length > 0) {...}
条件主要判断movie
包含actors
属性,并且此属性至少有一个actor
。
使用可选链接,同样代码也简洁了很了,如下:
function getLeadingActor(movie) { return movie.actors?.[0]?.name; } getLeadingActor(movieSmall); // => undefined getLeadingActor(movieFull); // => 'Harrison Ford'
actors?.
确保actors
属性存在, [0]?.
确保列表中存在第一个actor
。
3.nullish coalescing (空值合并?)
一个名为nullish coalescing operator的新提议? 处理undefined
或null
,将它们默认为特定值。
表达式变量??
如果变量undefined
或为null
,则默认值为指定的值。
const noValue = undefined; const value = 'Hello'; noValue ?? 'Nothing'; // => 'Nothing' value ?? 'Nothing'; // => 'Hello'
接着使用??
来优化一下 getLeading()
函数,当movie
对象中没有actor
时返回“Unknown actor
”
function getLeadingActor(movie) { return movie.actors?.[0]?.name ?? 'Unknown actor'; } getLeadingActor(movieSmall); // => 'Unknown actor' getLeadingActor(movieFull); // => 'Harrison Ford'
4. 可选链的三种形式
咱们可以使用以下3种形式的可选链。
第一种: object?.property
用于访问静态属性:
const object = null; object?.property; // => undefined
第二种:object?.[expression]
用于访问动态属性或数组项:
// 其一 const object = null; const name = 'property'; object?.[name]; // => undefined // 其二 const array = null; array?.[0]; // => undefined
第三种:object?.([arg1, [arg2, ...]])
执行一个对象方法
const object = null; object?.method('Some value'); // => undefined
将这三种组合起来创建一个可选链:
const value = object.maybeUndefinedProp?.maybeNull()?.[propName];
5.短路:遇到 null/undefined
停止
可选链接运算符的有趣之处在于,只要在左侧leftHandSide?.rightHandSide
遇到无效值,右侧访问就会停止,这称为短路。
看看例子:
const nothing = null; let index = 0; nothing?.[index++]; // => undefined index; // => 0
6. 何时使用可选链
不要急于使用可选的链操作符来访问任何类型的属性:这会导致错误的使用。
6.1访问潜在无效的属性
?.
一般使用在可能为空的属性:maybeNullish?.prop
。在确定属性不为空的情况下,使用属性访问器:.property或[propExpression]
。
// 好 function logMovie(movie) { console.log(movie.director?.name); console.log(movie.title); } // 不好 function logMovie(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
可选属性。
尝试使用可选的链操作符:
function hasPadding({ padding }) { const top = padding?.top ?? 0; const right = padding?.right ?? 0; const bottom = padding?.bottom ?? 0; const left = padding?.left ?? 0; return left + top + right + bottom !== 0; } hasPadding({ color: 'black' }); // => false hasPadding({ padding: { left: 0 } }); // => false hasPadding({ padding: { right: 10 }}); // => true
虽然函数正确地确定了元素是否有padding
,但是对于每个属性使用可选的链有点过于麻烦了。
更好的方法是使用对象扩展操作符将padding
对象默认为零值
function hasPadding({ padding }) { const p = { top: 0, right: 0, bottom: 0, left: 0, ...padding }; return p.top + p.left + p.right + p.bottom !== 0; } hasPadding({ color: 'black' }); // => false hasPadding({ padding: { left: 0 } }); // => false hasPadding({ padding: { right: 10 }}); // => true
这个就比可选链来的更简洁。