原文連接:5 Best Practices to Write Quality Arrow Functions, by Dmitri Pavlutinjavascript
箭頭函數值得流行。它語法簡潔,函數體裏的 this
是詞法環境提供的,很是適合用做回調函數。java
本篇文章,你講學習到 5 個 編寫高質量箭頭函數的最佳實踐。json
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
增長了計數器變量的值行內函數(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);
複製代碼
這就是個人建議:
「當箭頭函數只包含一個表達式的時候,一種好的作法是使用箭頭函數的行內形式。」
比較運算符 >
、<
、<=
和 >=
跟胖箭頭 =>
(定義箭頭函數時使用的)很像。
當這些比較運算符跟行內箭頭函數一塊兒使用的時候,會有點混亂。
舉個例子:
const negativeToZero = number => number <= 0 ? 0 : number;
複製代碼
一行裏的 =>
和 <=
容器產生誤導。
爲了清楚地將胖箭頭與比較運算符區分開,第一種選擇是將表達式包裝在一對括號中:
const negativeToZero = number => (number <= 0 ? 0 : number);
複製代碼
第二個選擇是故意使用箭頭函數的長寫法形式:
const negativeToZero = number => {
return number <= 0 ? 0 : number;
};
複製代碼
重構以後的代碼能夠消除胖箭頭與比較運算符之間的混淆。
若是箭頭函數中包含使用
>
、<
、<=
、>=
運算符的表達式,一個好的辦法是將表達式包裝在一對括號中,或者使用箭頭函數的長寫法形式。
行內箭頭函數中的對象字面量會觸發語法錯誤:
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'
}));
複製代碼
個人建議:
「在行內箭頭函數中使用時,將對象字面量包裝在一對括號中。」
箭頭函數的語法很短、很好。 但反作用是,當過多嵌套箭頭函數使用時,代碼可能就變得晦澀難懂了。
讓咱們考慮如下狀況——單擊按鈕,將發起一個對服務器的請求,響應準備就緒後,將返回數據記錄在控制檯:
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);
複製代碼
重構以後,咱們使用了兩個變量 readItemsJson
和 handleButtonClick
保存箭頭函數,嵌套級別從 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
語法。
(正文完)
廣告時間(長期有效)
我有一位好朋友開了一間貓舍,在此幫她宣傳一下。如今貓舍裏養的都是布偶貓。若是你也是個愛貓人士而且有須要的話,不妨掃一掃她的【閒魚】二維碼。不買也沒關係,看看也行。
(完)