JS可選鏈的好處

做者:Dmitri Pavlutinjavascript

譯者:前端小智html

來源:dmitripavlutin前端


阿里雲最近在作活動,低至2折,有興趣能夠看看promotion.aliyun.com/ntms/yunpar…java


爲了保證的可讀性,本文采用意譯而非直譯。git

JS的一些特性極大地改變了我們的編碼方式。從ES6年開始,對我們代碼影響最大的特性的解 、箭頭函數、類和模塊系統。github

到2019年8月,一個新的可選鏈提案已經進入第三階段,這是一個很好的改進。可選連接改變了從深層對象結構訪問屬性的方式。express

來看看這是又是什麼騷操做。數組

這個禮拜《大遷世界》有抽獎活動,獎品:專欄 《左耳聽風》 x3, 技術書 x5,歡迎關注回覆:抽獎安全

問題

因爲JS的動態特性,對象能夠具備多層不一樣的嵌套對象結構。數據結構

一般,當我們處理如下這些對象時:

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

雖然JS爲對象支持不一樣層次數據結構,可是在訪問此類對象的屬性時,複雜性也隨着增長。

bigObject能夠在運行時擁有不一樣的屬性集

// 嵌套版本
const bigObject = {
  // ...
  prop1: {
    //...
    prop2: {
      // ...
      value: 'Some value'
    }
  }
};

// 簡單版本
const bigObject = {
  // ...
  prop1: {
    // Nothing here   
  }
};
複製代碼

所以,必須手動檢查屬性是否存在

if (bigObject && 
    bigObject.prop1 != null && 
    bigObject.prop1.prop2 != null) {
  let result = bigObject.prop1.prop2.value;
}
複製代碼

這樣寫太過冗長了,最好避免寫它。

我們來看看可選鏈如何解決這個問題,以減小冗餘的代碼。

2. 易於深刻訪問屬性

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

movieSmall對象僅包含title,而movieFull包含完整的屬性集:

const movieSmall = {
  title: 'Heat'
};

const movieFull = {
  title: 'Blade Runner',
  director: { name: 'Ridley Scott' },
  actors: [{ name: 'Harrison Ford' }, { name: 'Rutger Hauer' }]
};
複製代碼

寫一個獲取director的函數。 請記住,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時,JS會拋出TypeError: Cannot read property 'name' of undefined

這種場景最適合使用可選鏈的功能了,以下所示,代碼將簡潔不少。

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

簡單地說,代碼片斷:

let name = movie.director?.name;
複製代碼

等價於

let name;
if (movie.director != null) {
  name = movie.director.name;
}
複製代碼

?.經過減小兩行代碼簡化getDirector()函數,這就是爲何我喜歡可選鏈的緣由。

2.1 數組項

可選的鏈功能能夠作得更多。能夠自由地在同一個表達式中使用多個可選的連接操做符,甚至可使用它安全地訪問數組項。

下一個任務是編寫一個函數,返回電影的actors中的name

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

使用可選連接,一樣代碼也簡潔了很了,以下:

function getLeadingActor(movie) {
  return movie.actors?.[0]?.name;
}

getLeadingActor(movieSmall); // => undefined
getLeadingActor(movieFull);  // => 'Harrison Ford'
複製代碼

actors?. 確保actors屬性存在, [0]?.確保列表中存在第一個actor

3.雙問號操做符

一個名爲nullish coalescing operator的新提議? 處理undefinednull,將它們默認爲特定值。

表達式變量?? 若是變量undefined或爲null,則默認值爲指定的值。

const noValue = undefined;
const value = 'Hello';

noValue ?? 'Nothing'; // => 'Nothing'
value   ?? 'Nothing'; // => 'Hello'
複製代碼

接着使用??來優化一下 getLeading()函數,當movie對象中沒有actor時返回「Unknown actor

function getLeadingActor(movie) {
  return movie.actors?.[0]?.name ?? 'Unknown actor';
}

getLeadingActor(movieSmall); // => 'Unknown actor'
getLeadingActor(movieFull);  // => 'Harrison Ford'
複製代碼

4. 可選鏈的三種形式

我們可使用如下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
複製代碼

6. 什麼時候使用可選鏈

不要急於使用可選的鏈操做符來訪問任何類型的屬性:這會致使錯誤的使用。

6.1訪問潛在無效的屬性

?.通常使用在可能爲空的屬性:maybeNullish?.prop。在肯定屬性不爲空的狀況下,使用屬性訪問器:.property或[propExpression]

// 好
function logMovie(movie) {
  console.log(movie.director?.name);
  console.log(movie.title);
}

// 很差
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
複製代碼

雖然函數正確地肯定了元素是否有padding,可是對於每一個屬性使用可選的鏈有點過於麻煩了。

更好的方法是使用對象擴展操做符將padding對象默認爲零值

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
複製代碼

這個就比可選鏈來的更簡潔。

代碼部署後可能存在的BUG無法實時知道,過後爲了解決這些BUG,花了大量的時間進行log 調試,這邊順便給你們推薦一個好用的BUG監控工具 Fundebug

原文:dmitripavlutin.com/javascript-…

交流(歡迎加入羣,羣工做日都會發紅包,互動討論技術)

乾貨系列文章彙總以下,以爲不錯點個Star,歡迎 加羣 互相學習。

github.com/qq449245884…

我是小智,公衆號「大遷世界」做者,對前端技術保持學習愛好者。我會常常分享本身所學所看的乾貨,在進階的路上,共勉!

關注公衆號,後臺回覆福利,便可看到福利,你懂的。

相關文章
相關標籤/搜索