作者:Dmitri Pavlutin翻譯:瘋狂的技術宅
原文:https://dmitripavlutin.com/ja...
未經允許嚴禁轉載
JavaScript 的特性極大地改變了你的編碼方式。從 ES2015 開始,對我程式碼影響最多的功能是解構、箭頭函式、類和模組系統。
截至 2019 年 8 月,一項新提案 optional chaining 達到了第3階段,這將是一個很好的改進。Optional Chaining 改變了從深層物件結構訪問屬性的方式。
下面讓我們來看看 optional chaining 是如何通過在深度訪問可能缺少的屬性時刪除樣板條件和變數來簡化程式碼的。
1. 問題
由於 JavaScript 的動態特性,物件可以有區別很大的巢狀物件結構。
通常,你在以下情況下處理此類物件:
- 獲取遠端 JSON 資料
- 使用配置物件
- 具有 optional 屬性
雖然這為物件提供了支援不同結構資料的靈活性,但是在訪問這些物件的屬性時會增加複雜性。
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 如何解決這個問題,並減少樣板條件。
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
屬性可能會不存在:
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 的原因。
2.1 陣列項
但是 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 存在於列表中。很好!
3. nullish 合併
名為 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'
4. optional chaining 的 3 種形式
可以用以下 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];
5. 短路:停止於 null/undefined
有關 optional chaining 運算子的有趣之處在於,只要在其左側 leftHandSide?.rightHandSide
中遇到無效值,右側訪問器的評估就會停止。這稱為短路。
我們來看一個例子:
const nothing = null;
let index = 0;
nothing?.[index++]; // => undefined
index; // => 0
nothing
保持一個 nullish 值,因此 optional chaining 評估為 undefined
,並跳過右側訪問器的評估。因為 index
編號不會增加。
6. 何時使用 optional chaining
一定要剋制使用 optional chaining 操作符訪問任何型別屬性的衝動:這將會導致誤導使用。下一節將介紹何時正確使用它。
6.1 訪問可能無效的屬性
?.
必須只在可能無效的屬性附近使用: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);
}
6.2 通常有更好的選擇
以下函式 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()
更容易閱讀。
7. 為什麼我喜歡它?
我喜歡 optional chaining 運算子,因為它允許從巢狀物件輕鬆訪問屬性。它可以減少通過編寫樣板檔案來驗證來自訪問器鏈的每個屬性訪問器上無效值的工作。
當 optional chaining 與無效合併運算子組合時,你可以獲得更好的結果,能夠更輕鬆地處理預設值。
本文首發微信公眾號:前端先鋒
歡迎掃描二維碼關注公眾號,每天都給你推送新鮮的前端技術文章
歡迎繼續閱讀本專欄其它高贊文章:
- 深入理解Shadow DOM v1
- 一步步教你用 WebVR 實現虛擬現實遊戲
- 13個幫你提高開發效率的現代CSS框架
- 快速上手BootstrapVue
- JavaScript引擎是如何工作的?從呼叫棧到Promise你需要知道的一切
- WebSocket實戰:在 Node 和 React 之間進行實時通訊
- 關於 Git 的 20 個面試題
- 深入解析 Node.js 的 console.log
- Node.js 究竟是什麼?
- 30分鐘用Node.js構建一個API伺服器
- Javascript的物件拷貝
- 程式設計師30歲前月薪達不到30K,該何去何從
- 14個最好的 JavaScript 資料視覺化庫
- 8 個給前端的頂級 VS Code 擴充套件外掛
- Node.js 多執行緒完全指南
- 把HTML轉成PDF的4個方案及實現