做者:Dmitri Pavlutin翻譯:瘋狂的技術宅javascript
原文:https://dmitripavlutin.com/ja...前端
未經容許嚴禁轉載java
JavaScript 的特性極大地改變了你的編碼方式。從 ES2015 開始,對我代碼影響最多的功能是解構、箭頭函數、類和模塊系統。git
截至 2019 年 8 月,一項新提案 optional chaining 達到了第3階段,這將是一個很好的改進。Optional Chaining 改變了從深層對象結構訪問屬性的方式。程序員
下面讓咱們來看看 optional chaining 是如何經過在深度訪問可能缺乏的屬性時刪除樣板條件和變量來簡化代碼的。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; }
這會產生不少樣板代碼。若是不須要寫這些代碼那就太好了。
讓咱們看看 optional chaining 如何解決這個問題,並減小樣板條件。
讓咱們設計一個保存電影信息的對象。該對象包含一個 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
屬性可能會不存在:
function getDirector(movie) { if (movie.director != null) { return movie.director.name; } } getDirector(movieSmall); // => undefined getDirector(movieFull); // => 'Ridley Scott'
if (movie.director) {...}
條件用於驗證 director
屬性是否已定義。若是沒有這個預防措施,在訪問movieSmall
對象 director
的時候,JavaScript 會拋出錯誤 TypeError: Cannot read property 'name' of undefined
。
這是使用新的 optional chaining 功能的正確位置,並刪除 movie.director
的存在驗證。新版本的getDirector()
看起來要短得多:
function getDirector(movie) { return movie.director?.name; } getDirector(movieSmall); // => undefined getDirector(movieFull); // => 'Ridley Scott'
在表達式 movie.director?.name
中你能夠找到 ?.
: optional chaining 運算符。
在 movieSmall
的狀況下,若是屬性 director
丟失了。那麼 movie.director?.name
的計算結果爲 undefined
。 optional chaining 運算符可防止拋出 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; }
?.
經過減小 2 行代碼簡化了 getDirector()
函數。這就是我喜歡 optional chaining 的緣由。
可是 optional chaining 功能能夠作更多的事情。你能夠在同一表達式中使用多個optional chaining 運算符。甚至可使用它來安全地訪問數組項目!
接下來的任務是編寫一個返回電影主角名字的函數。
在 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。
經過使用 optional chaining,此任務很容易解決:
function getLeadingActor(movie) { return movie.actors?.[0]?.name; } getLeadingActor(movieSmall); // => undefined getLeadingActor(movieFull); // => 'Harrison Ford'
actors?.
確保 actors
屬性存在。 [0]?.
確保第一個 actor 存在於列表中。很好!
名爲 nullish coalescing operator 的新提案建議用 ??
處理 undefined
或null
,將它們默認爲特定的值。
若是 variable
是undefined
或null
,則表達式 variable ?? defaultValue
的結果爲defaultValue
, 不然表達式的值爲variable
的值。
const noValue = undefined; const value = 'Hello'; noValue ?? 'Nothing'; // => 'Nothing' value ?? 'Nothing'; // => 'Hello'
當評估爲 undefined
時,Nullish 合併能夠經過默認值來改進 optional chaining。
例如,當 movie 對象中沒有 actor時,讓咱們改變 getLeading()
函數返回 "Unknown actor"
:
function getLeadingActor(movie) { return movie.actors?.[0]?.name ?? 'Unknown actor'; } getLeadingActor(movieSmall); // => 'Unknown actor' getLeadingActor(movieFull); // => 'Harrison Ford'
能夠用如下 3 種形式使用 optional chaining 。
第一種形式 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];
有關 optional chaining 運算符的有趣之處在於,只要在其左側 leftHandSide?.rightHandSide
中遇到無效值,右側訪問器的評估就會中止。這稱爲短路。
咱們來看一個例子:
const nothing = null; let index = 0; nothing?.[index++]; // => undefined index; // => 0
nothing
保持一個 nullish 值,所以 optional chaining 評估爲 undefined
,並跳過右側訪問器的評估。由於 index
編號不會增長。
必定要剋制使用 optional chaining 操做符訪問任何類型屬性的衝動:這將會致使誤導使用。下一節將介紹什麼時候正確使用它。
?.
必須只在可能無效的屬性附近使用:maybeNullish?.prop
。在其餘狀況下,使用舊的屬性訪問器:.property
或 [propExpression]
。
回想一下 movie 對象。查看錶達式 movie.director?.name
,因 爲director
能夠是 undefined
,在director
屬性附近使用 optional chaining 運算符是正確的。
相反,使用 ?.
來訪問電影標題是沒有意義的:movie?.title
。movie 對象不會是無效的。
// 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
。
下面嘗試使用 optional chaining 運算符:
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
雖然函數正確地肯定元素是否具備填充,可是對於每一個屬性都使用 optional chaining 是很是困難的。
更好的方法是使用對象擴展運算符將填充對象默認爲零值:
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()
更容易閱讀。
我喜歡 optional chaining 運算符,由於它容許從嵌套對象輕鬆訪問屬性。它能夠減小經過編寫樣板文件來驗證來自訪問器鏈的每一個屬性訪問器上無效值的工做。
當 optional chaining 與無效合併運算符組合時,你能夠得到更好的結果,可以更輕鬆地處理默認值。