[譯]JS箭頭函數三連問:爲什麼用、怎麼用、什麼時候用

在現代JS中最讓人期待的特性就是關於箭頭函數,用=>來標識。箭頭函數有兩個主要的優勢:其一是很是簡明的語法,另外就是直觀的做用域和this的綁定。javascript

由於這些優勢,箭頭函數比起其餘形式的函數聲明更加受歡迎。好比,受歡迎的airbnb eslint configuration庫會強制使用JavaScript箭頭函數建立匿名函數。vue

然而,就像世間萬物同樣,箭頭函數有一些優勢也有一些「缺點」,這就須要在使用的時候作一些權衡了。java

學習如何權衡是使用好箭頭函數的關鍵。在這篇文章中咱們將回顧箭頭函數是怎樣工做的,而後深刻探討,實際代碼中箭頭函數是如何改進咱們代碼的,以及一些箭頭函數不推薦的狀況。python

什麼纔是箭頭函數

JS的箭頭函數大概就像python中的lambda(python定義匿名函數的關鍵字)和ruby中的blocks(相似於閉包)同樣。 這些匿名函數都有他們特殊的語法:首先接收必定數目的參數,而後在定義它們的函數的做用域或就近做用域中執行。jquery

接下來咱們將詳細探討這些。數組

箭頭函數的語法

箭頭函數有一個大致的結構,同時也有不少的特殊狀況能夠簡化。 核心的結構以下:promise

(argument1, argument2, ... argumentN) => {
    // function body
}
複製代碼

在括號裏面有一系列的參數,接着跟着一個箭頭符號=>,最後是函數體。這跟傳統的函數很相像,只是咱們省略了function關鍵字,而且添加了一個=>在參數後面。ruby

而且,這裏也有不少種狀況,讓箭頭函數結構變得更加的簡潔。閉包

首先,若是函數體裏面是一個單獨的表達式,你能夠省略大括號直接將表達式寫在一行,而且表達式的結果也將會被函數直接返回。好比:app

const add = (a, b) => a + b;
複製代碼

其次,若是這傳入的是一個單獨的參數,你也可省略參數部分的括號。好比:

const getFirst = array => array[0];
複製代碼

如你所見,這樣就看起來更加的簡潔了,咱們也將在後面說明更多的特性。

高級語法

若是你瞭解這些高級語法以後將十分受用。

首先,若是你嘗試在一行書寫函數,可是返回的值倒是一個對象內容,你原想這樣寫:

(name, description) => {name: name, description: description};
複製代碼

而問題就是這樣的語法會引發歧義,會誤覺得你在寫一個函數的函數體。 若是想返回的是單個的對象,請用括號包裝該對象:

(name, description) => ({name: name, description: description});
複製代碼

封閉的上下文做用域

不像其餘形式的函數,箭頭函數並無他們本身的執行上下文。實際上,這就意味着代碼中的thisarguments都是繼承自他們的父函數。

好比,比較下面箭頭函數和傳統函數的區別:

const test = {
  name: 'test object',
  createAnonFunction: function() {
    return function() {
      console.log(this.name);
      console.log(arguments);
    };
  },
  createArrowFunction: function() {
    return () => {
      console.log(this.name);
      console.log(arguments);
    };
  }
};
複製代碼

咱們有一個有兩個方法的對象,每一個方法都返回了一個匿名函數。區別在於第一個方法裏面用了傳統的函數表達式,後面的用了箭頭函數表達式。若是咱們在傳入一樣的參數運行,咱們獲得了兩個不一樣的結果。

const anon = test.createAnonFunction('hello', 'world');
//返回匿名函數
const arrow = test.createArrowFunction('hello', 'world');
anon();
//undefined
//{}
// this->window
arrow();
//test object
//object { '0': 'hello', '1': 'world' }
//this->test
複製代碼

第一個匿名函數有本身的上下文(指向並不是test對象),當你調用的時候沒有參考的this.name的屬性,(注意:如今this指向window),也沒有建立它時調用的參數。另外一個,箭頭函數與建立它的函數有相同的上下文,讓其能夠訪問參數arguments和對象。

箭頭函數改進您的代碼

傳統lambda函數的主要用例之一,就是將函數用於數組的遍歷,如今用JavaScript箭頭函數實現。 好比你有一個有值的數組,你想去map遍歷每一項,這時箭頭函數是很是推薦的:

const words = ['hello', 'WORLD', 'Whatever'];
const downcasedWords = words.map(word => word.toLowerCase());
複製代碼

一個及其常見的例子就是返回一個對象的某個值:

const names = objects.map(object => object.name);
複製代碼

相似的,當用forEach來替換傳統for循環的時候,實際上箭頭函數會直觀的保持this來自於父一級

this.examples.forEach(example => {
  this.runExample(example);
});
複製代碼

Promise和Promise鏈

當在編寫異步程序的時候,箭頭函數也會讓代碼更加直觀和簡潔。

Promise能夠更簡單的編寫異步程序。雖然你樂意去使用async/await,你也須要好好理解promise,由於這是他們的基礎。

使用promise,仍然須要定義你的代碼執行完成以後的回調函數。 這是箭頭函數的理想位置,特別是若是您生成的函數是有狀態的,同時想引用對象中的某些內容。

this.doSomethingAsync().then((result) => {
  this.storeResult(result);
});
複製代碼

對象轉換

箭頭函數的另外一個常見並且十分有用的地方就是用於封裝的對象轉換。 例如在Vue.js中,有一種通用模式,就是使用mapState將Vuex存儲的各個部分,直接包含到Vue組件中。 這涉及到定義一套mappers,用於從原對象到完整的轉換輸出,這在組件問題中實十分有必要的。 這一系列簡單的轉換,使用箭頭函數是最合適不過的。好比:

export default {
  computed: {
    ...mapState({
      results: state => state.results,
      users: state => state.users,
    });
  }
}
複製代碼

你不該該使用箭頭函數的情景

這裏有許多箭頭函數不推薦的場景,這種狀況之下不只沒有幫助,並且還會形成沒必要要的麻煩。

首先就是對象中的方法。這裏有一個函數上下文的例子,對於咱們理解頗有幫助。 曾經流行一種趨勢,用class類的語法和箭頭函數,爲其自動綁定方法。好比:事件方法可使用,可是仍然綁定在class類中。 看起來就像下面的例子:

class Counter {
  counter = 0;
  handleClick = () => {
    this.counter++;
  }
}
複製代碼

在這種方法中,若是被一個點擊事件函數調用了,它雖然不是Counter的上下文中,它仍舊能夠訪問實例的數據,這種方式的缺點不言而喻。

用這種方式的確提供了一種綁定函數的快捷方式,可是函數的表達形式多種多樣,至關不直觀。若是你嘗試在原型使用這種對象,這將不利於測試,同時也會產生不少問題。 相反,推薦用一種常規的綁定方式,若有必要能夠綁定在實例的構造函數中:

class Counter {
  counter = 0;
  handleClick() {
    this.counter++;
  }
  constructor() {
    this.handleClick = this.handleClick.bind(this);
  }
}
複製代碼

深層調用

另外一種使用箭頭函數會讓你頭疼的地方,就是你去用不少函數的組合調用,尤爲是函數的深層調用。 簡單的理由跟匿名函數同樣,堆棧的追蹤很複雜。

若是你的函數僅僅在一層之下,而不是深層的迭代,這倒不是什麼問題。可是若是你將函數定義爲箭頭函數,而且在他們之間來回調用,當你調試bug的時候你將被代碼困惑,甚至獲得以下的錯誤信息:

{anonymous}()
{anonymous}()
{anonymous}()
{anonymous}()
{anonymous}()
//anonymous 匿名
複製代碼

有動態上下文的函數

還有最有一種箭頭函數會讓你困惑的情形,就是this是動態綁定的時候。 若是你在如下情形使用箭頭函數,那麼this的動態綁定不會如期工做,而且你也會困惑這些代碼爲何不像預期那樣工做,也會給你以後工做的人形成麻煩。 一些典型的例子:

  • 事件的調用函數,this指向當前的目標屬性
  • 在jquery中,大多數時候this指向的是當前被選擇的元素
  • 在vue中,methodscomputed中的this指向的是vue的組件。

固然你也能夠在上面的情形之下謹慎的使用箭頭函數。但特別是在jqueryvue的狀況下, 這一般會干擾正常功能, 並使您感到困惑:爲何看起來跟別人代碼同樣的代碼就是不工做。

總結

箭頭函數是JS語言中十分特別的屬性,而且使不少情形中代碼更加的變化莫測。儘管如此,就像其餘的語言特性,他們有各自的優缺點。所以咱們使用它應該僅僅是做爲一種工具,而不是無腦的簡單的所有替換爲箭頭函數。

本文只是我的興趣翻譯,若有錯誤之處還望各位斧正。文章版權屬於原文做者。

原文地址:codeburst.io/javascript-…

相關文章
相關標籤/搜索