[譯] 編寫高質量箭頭函數的 5 個最佳實踐

原文連接:5 Best Practices to Write Quality Arrow Functions, by Dmitri Pavlutinjavascript

箭頭函數值得流行。它語法簡潔,函數體裏的 this 是詞法環境提供的,很是適合用做回調函數。java

本篇文章,你講學習到 5 個 編寫高質量箭頭函數的最佳實踐。json

1. 箭頭函數名推理

JS 中的箭頭函數是 匿名的(anonymous):即函數的 name 屬性是個空字符串 ''服務器

( number => number + 1 ).name; // => ''
複製代碼

匿名函數在調試會話(debug session)或調用棧分析(call stack analysis)時被標記爲 anonymous。不幸的是,匿名函數並不能爲調試程序帶來有用的線索。session

下面展現了執行匿名函數代碼時的一個調試會話界面:async

右側的調用棧中(call stack)中包含兩個被標記爲 anonymous 的函數。這類調用棧信息沒法提供任何有用的線索。函數

幸運的是,箭頭函數名推理(function name inference,ES2015 提供的一個特性)能夠在某些條件下檢測到函數名稱。名稱推理的原理是 JS 能夠從其語法位置肯定箭頭函數名稱:例如,從保存函數對象的變量名稱中得到。學習

咱們來看看,箭頭函數名推理的例子:fetch

const increaseNumber = number => number + 1;

increaseNumber.name; // => 'increaseNumber'
複製代碼

increaseNumber 是保存箭頭函數對象的一個變量,JS 認爲 'increaseNumber' 能夠做爲箭頭函數的名字,原始箭頭函數就使用了這個名字。ui

「一個最佳實踐是使用函數名推理來命名箭頭函數。」

如今再來看看使用了名稱推理的調試會話界面:

由於箭頭函數具備名稱,因此調用堆棧能提供正在執行的代碼的更多信息:

  • handleButtonClick 代表發生了一個點擊事件
  • increaseCounter 增長了計數器變量的值

2. 儘量使用行內形式

行內函數(inline function)是指僅包含一個表達式的函數。我喜歡箭頭函數的行內書寫方式。

舉個例子:

const array = [1, 2, 3];

array.map((number) => { 
  return number * 2;
});
複製代碼

當箭頭函數裏只有一個表達式的時候,咱們不使用這種長形式(logn form),而是把括號 {}return 刪掉。

const array = [1, 2, 3];

array.map(number => number * 2);
複製代碼

這就是個人建議:

「當箭頭函數只包含一個表達式的時候,一種好的作法是使用箭頭函數的行內形式。」

3. 胖箭頭和比較運算符

比較運算符 ><<=>= 跟胖箭頭 =>(定義箭頭函數時使用的)很像。

當這些比較運算符跟行內箭頭函數一塊兒使用的時候,會有點混亂。

舉個例子:

const negativeToZero = number => number <= 0 ? 0 : number;
複製代碼

一行裏的 => 和 <= 容器產生誤導。

爲了清楚地將胖箭頭與比較運算符區分開,第一種選擇是將表達式包裝在一對括號中:

const negativeToZero = number => (number <= 0 ? 0 : number);
複製代碼

第二個選擇是故意使用箭頭函數的長寫法形式:

const negativeToZero = number => {
  return number <= 0 ? 0 : number;
};
複製代碼

重構以後的代碼能夠消除胖箭頭與比較運算符之間的混淆。

若是箭頭函數中包含使用 ><<=>= 運算符的表達式,一個好的辦法是將表達式包裝在一對括號中,或者使用箭頭函數的長寫法形式。

4. 正確返回對象字面量

行內箭頭函數中的對象字面量會觸發語法錯誤:

const array = [1, 2, 3];

// throws SyntaxError!
array.map(number => { 'number': number });
複製代碼

JS 把花括號當作代碼塊,而非對象字面量了。

用一對括號包裝對象字面量就能解決問題:

const array = [1, 2, 3];

// Works!
array.map(number => ({ 'number': number }));
複製代碼

若是對象字面量中包含不少屬性,還能夠使用換行符,同時還能保持箭頭函數的行內形式:

const array = [1, 2, 3];

// Works!
array.map(number => ({
  'number': number
  'propA': 'value A',
  'propB': 'value B'
}));
複製代碼

個人建議:

「在行內箭頭函數中使用時,將對象字面量包裝在一對括號中。」

5. 避免過多的嵌套

箭頭函數的語法很短、很好。 但反作用是,當過多嵌套箭頭函數使用時,代碼可能就變得晦澀難懂了。

讓咱們考慮如下狀況——單擊按鈕,將發起一個對服務器的請求,響應準備就緒後,將返回數據記錄在控制檯:

myButton.addEventListener('click', () => {
  fetch('/items.json')
    .then(response => response.json())
    .then(json => {
      json.forEach(item => {
        console.log(item.name);
      });
    });
});
複製代碼

箭頭函數是 3 級嵌套的。爲了看懂這樣的代碼,須要花些時間和精力。

爲了提升嵌套函數的可讀性,第一種方法是將每一個箭頭函數保存在一個變量中。該變量應簡明地描述函數的功能。

const readItemsJson = json => {
  json.forEach(item => console.log(item.name));
};

const handleButtonClick = () => {
  fetch('/items.json')
    .then(response => response.json())
    .then(readItemsJson);
};

myButton.addEventListener('click', handleButtonClick);
複製代碼

重構以後,咱們使用了兩個變量 readItemsJsonhandleButtonClick 保存箭頭函數,嵌套級別從 3 減小到 2。如今,能夠更輕鬆地瞭解代碼功能了。

進一步,你還能夠能夠 async/await 語法重構整個函數,這也是解決函數嵌套的一個好方法:

const handleButtonClick = async () => {
  const response = await fetch('/items.json');
  const json = await response.json();
  json.forEach(item => console.log(item.name));
};

myButton.addEventListener('click', handleButtonClick);
複製代碼

個人建議:

經過將箭頭函數保存在變量中的方式,能夠避免代碼的過多嵌套;同時,使用 async/await 語法,也能夠避免代碼的過多嵌套。

總結

JS 中的箭頭函數是匿名的。爲了使調試高效,一個好的實踐是使用變量來保存箭頭函數。如此,就能使用箭頭函數名推理特性了。

當函數主體只有一個表達式時,使用行內箭頭函數很是方便。

比較運算符 ><<=>= 跟胖箭頭 => 很像,使用行內箭頭函數時,能夠將表達式包裝在括號中幫助區分。

行內箭頭函數會把對象字面量語法 { prop: 'value' } 外的 {} 認做是代碼塊。所以,在行內箭頭函數中使用對象字面量時,須要使用括號包裝一下:() => ({ prop: 'value' })

最後,過多的箭頭函數嵌套會掩蓋代碼意圖。一種辦法是將箭頭函數提取到變量中。或者嘗試使用更好的特性,例如 async/await 語法。

(正文完)


廣告時間(長期有效)

我有一位好朋友開了一間貓舍,在此幫她宣傳一下。如今貓舍裏養的都是布偶貓。若是你也是個愛貓人士而且有須要的話,不妨掃一掃她的【閒魚】二維碼。不買也沒關係,看看也行。

(完)

相關文章
相關標籤/搜索