做者:Dmitri Pavlutin翻译:疯狂的技术宅javascript
原文:https://dmitripavlutin.com/ja...前端
未经容许严禁转载java
不少 JavaScript 的特性极大地改变了你的编码方式。从 ES2015 及更高版本开始,对个人代码影响最大的功能是解构、箭头函数、类和模块系统。git
截至2019年8月,一项新提案可选链(optional chaining)进入了第3阶段,将是一个很好的改进。可选的连接更改了从深层对象结构访问属性的方式。程序员
让咱们看看可选链是如何经过在深度访问可能缺乏的属性时删除样板条件和变量来简化代码的。github
因为 JavaScript 的动态特性,一个对象能够具备很是不一样的对象嵌套结构。面试
一般,你能够在如下状况下处理此类对象:express
尽管这为对象提供了支持不一样数据的灵活性,可是在访问此类对象的属性时,随之而来的是增长了复杂性。segmentfault
bigObject
在运行时能够有不一样的属性集:数组
// One version of bigObject const bigObject = { // ... prop1: { //... prop2: { // ... value: 'Some value' } } }; // Other version of bigObject const bigObject = { // ... prop1: { // Nothing here } };
所以你必须手动检查属性是否存在:
// Later if (bigObject && bigObject.prop1 != null && bigObject.prop1.prop2 != null) { let result = bigObject.prop1.prop2.value; }
最好不要这样写,由于包含了太多的样板代码。。
让咱们看看可选链是如何解决此问题,从而减小样板条件的。
让咱们设计一个保存电影信息的对象。该对象包含 title
必填属性,以及可选的 director
和 actor
。
movieSmall
对象仅包含 title
,而 movieFull
则包含完整的属性集:
const movieSmall = { title: 'Heat' }; const movieFull = { title: 'Blade Runner', director: { name: 'Ridley Scott' }, actors: [{ name: 'Harrison Ford' }, { name: 'Rutger Hauer' }] };
让咱们写一个获取导演姓名的函数。请注意 director
属性可能会丢失:
function getDirector(movie) { if (movie.director != null) { return movie.director.name; } } getDirector(movieSmall); // => undefined getDirector(movieFull); // => 'Ridley Scott'
if (movie.director) {...}
条件用于验证是否认义了 director
属性。若是没有这种预防措施,则在访问movieSmall
对象的导演的时,JavaScript 会引起错误 TypeError: Cannot read property 'name' of undefined
。
这是用了可选链功能并删除 movie.director
存在验证的正确位置。新版本的 getDirector()
看起来要短得多:
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
错误。
相反 movieFull
的属性 director
是可用的。 movie.director?.name
默认被评估为 'Ridley Scott'
。
简而言之,代码片断:
let name = movie.director?.name;
等效于:
let name; if (movie.director != null) { name = movie.director.name; }
?.
经过减小两行代码简化了 getDirector()
函数。这就是为何我喜欢可选链的缘由。
可选链能还能够作更多的事。你能够在同一表达式中自由使用多个可选链运算符。甚至能够用它安全地访问数组项!
下一个任务编写一个返回电影主角姓名的函数。
在电影对象内部,actor
数组能够为空甚至丢失,因此你必须添加其余条件:
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]?.
确保列表中存在第一个参与者。这真是个好东西!
一项名为nullish 合并运算符的新提案会处理 undefined
或 null
,将其默认设置为特定值。
若是 variable
是 undefined
或 null
,则表达式 variable ?? defaultValue
的结果为 defaultValue
。不然,表达式的计算结果为 variable
值。
const noValue = undefined; const value = 'Hello'; noValue ?? 'Nothing'; // => 'Nothing' value ?? 'Nothing'; // => 'Hello'
当链评估为 undefined
时,经过将默认值设置为零,Nullish 合并能够改善可选链。
例如,让咱们更改 getLeading()
函数,以在电影对象中没有演员时返回 "Unknown actor"
:
function getLeadingActor(movie) { return movie.actors?.[0]?.name ?? 'Unknown actor'; } getLeadingActor(movieSmall); // => 'Unknown actor' getLeadingActor(movieFull); // => 'Harrison Ford'
你能够经过如下 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];
可选链运算符的有趣之处在于,一旦在其左侧 leftHandSide?.rightHandSide
上遇到空值,就会中止对右侧访问器的评估。这称为短路。
看一个例子:
const nothing = null; let index = 0; nothing?.[index++]; // => undefined index; // => 0
nothing
保留一个空值,所以可选链当即求值为 undefined
,并跳过右侧访问器的求值。由于 index
的值没有增长。
要抵制使用可选链运算符访问任何类型属性的冲动:这会致使错误的用法。下一节将说明什么时候正确使用它。
必须仅在可能为空的属性附近使用 ?.
: maybeNullish?.prop
。在其余状况下,请使用老式的属性访问器:.property
或 [propExpression]
。
调用电影对象。查看表达式 movie.director?.name
,由于 director
能够是 undefined
,因此在 director
属性附近使用可选链运算符是正确的。
相反,使用 ?.
访问电影标题 movie?.title
没有任何意义。电影对象不会是空的。
// Good function logMovie(movie) { console.log(movie.director?.name); console.log(movie.title); } // Bad function logMovie(movie) { // director needs optional chaining console.log(movie.director.name); // movie doesn't need optional chaining console.log(movie?.title); }
如下函数 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
虽然函数能够正确地肯定元素是否具备填充,可是为每一个属性使用可选链是毫无必要的。
更好的方法是使用对象散布运算符将填充对象默认为零值:
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
我认为这一版本的 hasPadding()
可读性更好。
我喜欢可选链运算符,由于它容许轻松地从嵌套对象中访问属性。它能够防止编写针对访问者链中每一个属性访问器上的空值进行验证的样板代码。
当可选链与空值合并运算符结合使用时,能够获得更好的结果,从而更轻松地处理默认值。
你还知道哪些可选链的好案例?请在下面的评论中描述它!