新聞中心
JavaScript 的特性極大地改變了你的編碼方式。從 ES2015 開始,對(duì)我代碼影響最多的功能是解構(gòu)、箭頭函數(shù)、類和模塊系統(tǒng)。

創(chuàng)新新互聯(lián),憑借十余年的成都網(wǎng)站設(shè)計(jì)、成都網(wǎng)站建設(shè)、外貿(mào)網(wǎng)站建設(shè)經(jīng)驗(yàn),本著真心·誠心服務(wù)的企業(yè)理念服務(wù)于成都中小企業(yè)設(shè)計(jì)網(wǎng)站有上1000+案例。做網(wǎng)站建設(shè),選創(chuàng)新互聯(lián)建站。
截至 2019 年 8 月,一項(xiàng)新提案 optional chaining 達(dá)到了第3階段,這將是一個(gè)很好的改進(jìn)。Optional Chaining 改變了從深層對(duì)象結(jié)構(gòu)訪問屬性的方式。
下面讓我們來看看 optional chaining 是如何通過在深度訪問可能缺少的屬性時(shí)刪除樣板條件和變量來簡化代碼的。
1. 問題
由于 JavaScript 的動(dòng)態(tài)特性,對(duì)象可以有區(qū)別很大的嵌套對(duì)象結(jié)構(gòu)。
通常,你在以下情況下處理此類對(duì)象:
- 獲取遠(yuǎn)程 JSON 數(shù)據(jù)
- 使用配置對(duì)象
- 具有 optional 屬性
雖然這為對(duì)象提供了支持不同結(jié)構(gòu)數(shù)據(jù)的靈活性,但是在訪問這些對(duì)象的屬性時(shí)會(huì)增加復(fù)雜性。
bigObject 在運(yùn)行時(shí)可以有不同的屬性集:
- // One version of bigObject
- const bigObject = {
- // ...
- prop1: {
- //...
- prop2: {
- // ...
- value: 'Some value'
- }
- }
- };
- // Other version of bigObject
- const bigObject = {
- // ...
- prop1: {
- // Nothing here
- }
- };
因此,你必須手動(dòng)檢查屬性是否存在:
- // Later
- if (bigObject &&
- bigObject.prop1 != null &&
- bigObject.prop1.prop2 != null) {
- let result = bigObject.prop1.prop2.value;
- }
這會(huì)產(chǎn)生很多樣板代碼。如果不需要寫這些代碼那就太好了。
讓我們看看 optional chaining 如何解決這個(gè)問題,并減少樣板條件。
2. 輕松的深入訪問屬性
讓我們?cè)O(shè)計(jì)一個(gè)保存電影信息的對(duì)象。該對(duì)象包含一個(gè) title 屬性,以及可選的 director 和 actors。
movieSmall 對(duì)象只包含 title,而 movieFull 包含完整的屬性集:
- const movieSmall = {
- title: 'Heat'
- };
- const movieFull = {
- title: 'Blade Runner',
- director: { name: 'Ridley Scott' },
- actors: [{ name: 'Harrison Ford' }, { name: 'Rutger Hauer' }]
- };
讓我們寫一個(gè)獲取導(dǎo)演名字的函數(shù)。請(qǐng)記住,director 屬性可能會(huì)不存在:
- function getDirector(movie) {
- if (movie.director != null) {
- return movie.director.name;
- }
- }
- getDirector(movieSmall); // => undefined
- getDirector(movieFull); // => 'Ridley Scott'
if (movie.director) {...} 條件用于驗(yàn)證 director 屬性是否已定義。如果沒有這個(gè)預(yù)防措施,在訪問movieSmall 對(duì)象 director 的時(shí)候,JavaScript 會(huì)拋出錯(cuò)誤 TypeError: Cannot read property 'name' of undefined。
這是使用新的 optional chaining 功能的正確位置,并刪除 movie.director 的存在驗(yàn)證。新版本的getDirector()看起來要短得多:
- function getDirector(movie) {
- return movie.director?.name;
- }
- getDirector(movieSmall); // => undefined
- getDirector(movieFull); // => 'Ridley Scott'
在表達(dá)式 movie.director?.name 中你可以找到 ?.: optional chaining 運(yùn)算符。
在 movieSmall 的情況下,如果屬性 director 丟失了。那么 movie.director?.name 的計(jì)算結(jié)果為 undefined。 optional chaining 運(yùn)算符可防止拋出 TypeError:Cannot read property 'name' of undefined。
相反,在 movieFull 的情況下,屬性 director 可用。 movie.director?.name 的值為 'Ridley Scott'.。
簡單來說,代碼片段:
- let name = movie.director?.name;
相當(dāng)于:
- let name;
- if (movie.director != null) {
- name = movie.director.name;
- }
?. 通過減少 2 行代碼簡化了 getDirector() 函數(shù)。這就是我喜歡 optional chaining 的原因。
2.1 數(shù)組項(xiàng)
但是 optional chaining 功能可以做更多的事情。你可以在同一表達(dá)式中使用多個(gè)optional chaining 運(yùn)算符。甚至可以使用它來安全地訪問數(shù)組項(xiàng)目!
接下來的任務(wù)是編寫一個(gè)返回電影主角名字的函數(shù)。
在 movie 對(duì)象中,actors 數(shù)組可以為空甚至丟失,因此你必須添加其他條件:
- 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 屬性,并且此屬性至少有一個(gè) actor。
通過使用 optional chaining,此任務(wù)很容易解決:
- function getLeadingActor(movie) {
- return movie.actors?.[0]?.name;
- }
- getLeadingActor(movieSmall); // => undefined
- getLeadingActor(movieFull); // => 'Harrison Ford'
actors?. 確保 actors 屬性存在。 [0]?. 確保第一個(gè) actor 存在于列表中。很好!
3. nullish 合并
名為 nullish coalescing operator 的新提案建議用 ?? 處理 undefined或null,將它們默認(rèn)為特定的值。
如果 variable 是undefined或null,則表達(dá)式 variable ?? defaultValue 的結(jié)果為defaultValue, 否則表達(dá)式的值為variable 的值。
- const noValue = undefined;
- const value = 'Hello';
- noValue ?? 'Nothing'; // => 'Nothing'
- value ?? 'Nothing'; // => 'Hello'
當(dāng)評(píng)估為 undefined 時(shí),Nullish 合并可以通過默認(rèn)值來改進(jìn) optional chaining。
例如,當(dāng) movie 對(duì)象中沒有 actor時(shí),讓我們改變 getLeading() 函數(shù)返回 "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 用于訪問靜態(tài)屬性:
- const object = null;
- object?.property; // => undefined
第二種形式 object?.[expression] 用于訪問動(dòng)態(tài)屬性或數(shù)組項(xiàng):
- const object = null;
- const name = 'property';
- object?.[name]; // => undefined
- const array = null;
- array?.[0]; // => undefined
最后,第三種形式 object?.([arg1,[arg2,...]]) 執(zhí)行一個(gè)對(duì)象方法:
- const object = null;
- object?.method('Some value'); // => undefined
如果需要,可以通過組合這些表單來創(chuàng)建長的可選鏈:
- const value = object.maybeUndefinedProp?.maybeNull()?.[propName];
5. 短路:停止于 null/undefined
有關(guān) optional chaining 運(yùn)算符的有趣之處在于,只要在其左側(cè) leftHandSide?.rightHandSide 中遇到無效值,右側(cè)訪問器的評(píng)估就會(huì)停止。這稱為短路。
我們來看一個(gè)例子:
- const nothing = null;
- let index = 0;
- nothing?.[index++]; // => undefined
- index; // => 0
nothing 保持一個(gè) nullish 值,因此 optional chaining 評(píng)估為 undefined ,并跳過右側(cè)訪問器的評(píng)估。因?yàn)?index 編號(hào)不會(huì)增加。
6. 何時(shí)使用 optional chaining
一定要克制使用 optional chaining 操作符訪問任何類型屬性的沖動(dòng):這將會(huì)導(dǎo)致誤導(dǎo)使用。下一節(jié)將介紹何時(shí)正確使用它。
6.1 訪問可能無效的屬性
?. 必須只在可能無效的屬性附近使用:maybeNullish?.prop。在其他情況下,使用舊的屬性訪問器:.property 或 [propExpression]。
回想一下 movie 對(duì)象。查看表達(dá)式 movie.director?.name,因 為director 可以是 undefined,在director屬性附近使用 optional chaining 運(yùn)算符是正確的。
相反,使用 ?. 來訪問電影標(biāo)題是沒有意義的:movie?.title。movie 對(duì)象不會(huì)是無效的。
- // 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 通常有更好的選擇
以下函數(shù) hasPadding() 接受帶有可選 padding 屬性的樣式對(duì)象。 padding 具有可選屬性left、top、right、bottom。
下面嘗試使用 optional chaining 運(yùn)算符:
- 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
雖然函數(shù)正確地確定元素是否具有填充,但是對(duì)于每個(gè)屬性都使用 optional chaining 是非常困難的。
更好的方法是使用對(duì)象擴(kuò)展運(yùn)算符將填充對(duì)象默認(rèn)為零值:
- 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
在我看來,這個(gè)版本的 hasPadding() 更容易閱讀。
7. 為什么我喜歡它?
我喜歡 optional chaining 運(yùn)算符,因?yàn)樗试S從嵌套對(duì)象輕松訪問屬性。它可以減少通過編寫樣板文件來驗(yàn)證來自訪問器鏈的每個(gè)屬性訪問器上無效值的工作。
當(dāng) optional chaining 與無效合并運(yùn)算符組合時(shí),你可以獲得更好的結(jié)果,能夠更輕松地處理默認(rèn)值。
網(wǎng)頁名稱:為什么我喜歡JavaScript的OptionalChaining
當(dāng)前路徑:http://m.5511xx.com/article/dpsdiio.html


咨詢
建站咨詢
