爲何我喜歡 JavaScript 可選鏈

做者:Dmitri Pavlutin

翻譯:瘋狂的技術宅javascript

原文:https://dmitripavlutin.com/ja...前端

未經容許嚴禁轉載java

不少 JavaScript 的特性極大地改變了你的編碼方式。從 ES2015 及更高版本開始,對個人代碼影響最大的功能是解構、箭頭函數、類和模塊系統。git

截至2019年8月,一項新提案可選鏈(optional chaining)進入了第3階段,將是一個很好的改進。可選的連接更改了從深層對象結構訪問屬性的方式。程序員

讓咱們看看可選鏈是如何經過在深度訪問可能缺乏的屬性時刪除樣板條件和變量來簡化代碼的。github

1. 問題

因爲 JavaScript 的動態特性,一個對象能夠具備很是不一樣的對象嵌套結構。面試

一般,你能夠在如下狀況下處理此類對象:express

  • 獲取遠程JSON數據
  • 使用配置對象
  • 具備可選屬性

儘管這爲對象提供了支持不一樣數據的靈活性,可是在訪問此類對象的屬性時,隨之而來的是增長了複雜性。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;
}

最好不要這樣寫,由於包含了太多的樣板代碼。。

讓咱們看看可選鏈是如何解決此問題,從而減小樣板條件的。

2. 輕鬆深刻訪問屬性

讓咱們設計一個保存電影信息的對象。該對象包含 title 必填屬性,以及可選的 directoractor

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() 函數。這就是爲何我喜歡可選鏈的緣由。

2.1 數組項

可選鏈能還能夠作更多的事。你能夠在同一表達式中自由使用多個可選鏈運算符。甚至能夠用它安全地訪問數組項!

下一個任務編寫一個返回電影主角姓名的函數。

在電影對象內部,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]?. 確保列表中存在第一個參與者。這真是個好東西!

3. 默認爲Nullish合併

一項名爲nullish 合併運算符的新提案會處理 undefinednull ,將其默認設置爲特定值。

若是 variableundefinednull,則表達式 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'

4. 可選鏈的3種形式

你能夠經過如下 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];

5.短路:在null/undefined 處中止

可選鏈運算符的有趣之處在於,一旦在其左側 leftHandSide?.rightHandSide 上遇到空值,就會中止對右側訪問器的評估。這稱爲短路。

看一個例子:

const nothing = null;
let index = 0;

nothing?.[index++]; // => undefined
index;              // => 0

nothing 保留一個空值,所以可選鏈當即求值爲 undefined,並跳過右側訪問器的求值。由於 index 的值沒有增長。

6. 什麼時候使用可選鏈

要抵制使用可選鏈運算符訪問任何類型屬性的衝動:這會致使錯誤的用法。下一節將說明什麼時候正確使用它。

6.1 可能無效的訪問屬性

必須僅在可能爲空的屬性附近使用 ?.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);
}

6.2 一般有更好的選擇

如下函數 hasPadding() 接受具備可選 padding 屬性的樣式對象。 padding 具備可選的屬性 lefttoprightbottom

嘗試用可選鏈運算符:

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() 可讀性更好。

7. 我爲何喜歡它?

我喜歡可選鏈運算符,由於它容許輕鬆地從嵌套對象中訪問屬性。它能夠防止編寫針對訪問者鏈中每一個屬性訪問器上的空值進行驗證的樣板代碼。

當可選鏈與空值合併運算符結合使用時,能夠獲得更好的結果,從而更輕鬆地處理默認值。

你還知道哪些可選鏈的好案例?請在下面的評論中描述它!


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索