做者: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()
可讀性更好。
我喜歡可選鏈運算符,由於它容許輕鬆地從嵌套對象中訪問屬性。它能夠防止編寫針對訪問者鏈中每一個屬性訪問器上的空值進行驗證的樣板代碼。
當可選鏈與空值合併運算符結合使用時,能夠獲得更好的結果,從而更輕鬆地處理默認值。
你還知道哪些可選鏈的好案例?請在下面的評論中描述它!