Es6 箭頭函數

>>>> 1.引言javascript

箭頭符號在JavaScript誕生時就已經存在,當初第一個JavaScript教程曾建議在HTML註釋內包裹行內腳本,這樣能夠避免不支持JS的瀏覽器誤將JS代碼顯示爲文本。你會寫這樣的代碼:html

<script language="javascript">
    <!--
      document.bgColor = "brown";  // red
    // -->
    </script>

老式瀏覽器會將這段代碼解析爲兩個不支持的標籤和一條註釋,只有新式瀏覽器才能識別出其中的JS代碼。java

爲了支持這種奇怪的hack方式,瀏覽器中的JavaScript引擎將<!--這四個字符解析爲單行註釋的起始部分,我沒開玩笑,這自始至終就是語言的一部分,直到如今仍然有效,這種註釋符號不只出現<script>標籤後的首行,在JS代碼的每一個角落你都有可能見到它,甚至在Node中也是如此。git

碰巧,這種註釋風格(http://people.mozilla.org/~jorendorff/es6-draft.html#sec-html-like-comments)首次在ES6中被標準化了,但在新標準中箭頭被用來作其它事情。程序員

箭頭序列-->一樣是單行註釋的一部分。古怪的是,在HTML中-->以前的字符是註釋的一部分,而在JS中-->以後的部分纔是註釋。es6

你必定感到陌生的是,只有當箭頭在行首時纔會註釋當前行。這是由於在其它上下文中,-->是一個JS運算符:「趨向於」運算符!github

function countdown(n) {
      while (n --> 0)  // "n goes to zero"
        alert(n);
      blastoff();
    }

上面這段代碼能夠正常運行(http://codepen.io/anon/pen/oXZaBY?editors=001),循環會一直重複直到n趨於0,這固然不是ES6中的新特性,它只不過是將兩個你早已熟悉的特性經過一些誤導性的手段結合在一塊兒。你能理解麼?一般來講,相似這種謎團均可以在Stack Overflow(http://stackoverflow.com/questions/1642028/what-is-the-name-of-the-operator上找到答案。編程

固然,一樣地,小於等於操做符<=也形似箭頭,你能夠在JS代碼、隱藏的圖片樣式中找到更多相似的箭頭,可是咱們就不繼續尋找了,你應該注意到咱們漏掉了一種特殊的箭頭。瀏覽器

col 1 col 2
<!-- 單行註釋
--> 「趨向於」操做符
<= 小於等於
=> 這又是什麼?

=>究竟是什麼?咱們今天就來一探究竟。函數

首先,咱們談論一些有關函數的事情。

>>>> 2. 函數表達式無處不在

JavaScript中有一個有趣的特性,不管什麼時候,當你須要一個函數時,你均可以在想添加的地方輸入這個函數。

舉個例子,假設你嘗試告訴瀏覽器用戶點擊一個特定按鈕後的行爲,你會這樣寫:

$("#confetti-btn").click(

jQuery的.click()方法接受一個參數:一個函數。沒問題,你能夠在這裏輸入一個函數:

$("#confetti-btn").click(function (event) {
      playTrumpet();
      fireConfettiCannon();
    });

對 於如今的咱們來講,寫出這樣的代碼至關天然,而回憶起在這種編程方式流行以前,這種寫法相對陌生一些,許多語言中都沒有這種特性。1958年,Lisp首 先支持函數表達式,也支持調用lambda函數,而C++,Python、C#以及Java在隨後的多年中一直不支持這樣的特性。

如今大相徑庭,全部的四種語言都已支持lambda函數,更新出現的語言廣泛都支持內建的lambda函數。咱們必需要感謝JavaScript和早期的JavaScript程序員,他們勇敢地構建了重度依賴lambda函數的庫,讓這種特性被普遍接受。

使人傷感的是,隨後在全部我說起的語言中,只有JavaScript的lambda的語法最終變得冗長乏味。

// 六種語言中的簡單函數示例
    function (a) { return a > 0; } // JS
    [](int a) { return a > 0; }  // C++
    (lambda (a) (> a 0))  ;; Lisp
    lambda a: a > 0  # Python
    a => a > 0  // C#
    a -> a > 0  // Java

>>>> 3. 箭袋中的新羽

ES6中引入了一種編寫函數的新語法

// ES5
    var selected = allJobs.filter(function (job) {
      return job.isSelected();
    });
    // ES6
    var selected = allJobs.filter(job => job.isSelected());

當你只須要一個只有一個參數的簡單函數時,可使用新標準中的箭頭函數,它的語法很是簡單:標識符=>表達式。你無需輸入functionreturn,一些小括號、大括號以及分號也能夠省略。

(我我的對於這個特性很是感激,再也不須要輸入function這幾個字符對我而言相當重要,由於我老是不可避免地錯誤寫成functoin,而後我就不得不回過頭改正它。)

若是要寫一個接受多重參數(也可能沒有參數,或者是不定參數、默認參數:http://www.infoq.com/cn/articles/es6-in-depth-rest-parameters-and-defaults、參數解構:http://www.infoq.com/cn/articles/es6-in-depth-destructuring)的函數,你須要用小括號包裹參數list。

// ES5
    var total = values.reduce(function (a, b) {
      return a + b;
    }, 0);
    // ES6
    var total = values.reduce((a, b) => a + b, 0);

我認爲這看起來酷斃了。

正如你使用相似Underscore.js](http://underscorejs.org/)和Immutable.js(https://facebook.github.io/immutable-js/)這樣的庫提供的函數工具,箭頭函數運行起來一樣美不可言。事實上,Immutable的文檔(https://facebook.github.io/immutable-js/docs/#/)中的示例全都由ES6寫成,其中的許多特性已經用上了箭頭函數。

那麼不是很是函數化的狀況又如何呢?除表達式外,箭頭函數還能夠包含一個塊語句。回想一下咱們以前的示例:

// ES5
    $("#confetti-btn").click(function (event) {
      playTrumpet();
      fireConfettiCannon();
    });

這是它們在ES6中看起來的樣子:

// ES6
    $("#confetti-btn").click(event => {
      playTrumpet();
      fireConfettiCannon();
    });

這是一個微小的改進,對於使用了Promises(https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)的代碼來講箭頭函數的效果能夠變得更加戲劇性,}).then(function (result) { 這樣的一行代碼能夠堆積起來。

注意,使用了塊語句的箭頭函數不會自動返回值,你須要使用return語句將所需值返回。

小提示:當使用箭頭函數建立普通對象時,你老是須要將對象包裹在小括號裏。

// 爲與你玩耍的每個小狗建立一個新的空對象
    var chewToys = puppies.map(puppy => {});   // 這樣寫會報Bug!
    var chewToys = puppies.map(puppy => ({})); //

用小括號包裹空對象就能夠了。

不幸的是,一個空對象{}和一個空的塊{}看起來徹底同樣。ES6中的規則是,緊隨箭頭的{被解析爲塊的開始,而不是對象的開始。所以,puppy => {}這段代碼就被解析爲沒有任何行爲並返回undefined的箭頭函數。

更使人困惑的是,你的JavaScript引擎會將相似{key: value}的對象字面量解析爲一個包含標記語句的塊。幸運的是,{是惟一一個有歧義的字符,因此用小括號包裹對象字面量是惟一一個你須要牢記的小竅門。

>>>> 4. 這個函數的this值是什麼呢?

普通function函數和箭頭函數的行爲有一個微妙的區別,箭頭函數沒有它本身的this,箭頭函數內的this值繼承自外圍做用域。

在咱們嘗試說明這個問題前,先一塊兒回顧一下。

JavaScript中的this是如何工做的?它的值從哪裏獲取?這些問題的答案可都不簡單(http://stackoverflow.com/questions/3127429/how-does-the-this-keyword-work),若是你對此倍感清晰,必定由於你長時間以來一直在處理相似的問題。

這個問題常常出現的其中一個緣由是,不管是否須要,function函數總會自動接收一個this值。你是否寫過這樣的hack代碼:

{
      ...
      addAll: function addAll(pieces) {
        var self = this;
        _.each(pieces, function (piece) {
          self.add(piece);
        });
      },
      ...
    }

在這裏,你但願在內層函數裏寫的是this.add(piece),不幸的是,內層函數並未從外層函數繼承this的值。在內層函數裏,this會是windowundefined,臨時變量self用來將外部的this值導入內部函數。(另外一種方式是在內部函數上執行.bind(this),兩種方法都不甚美觀。)

在ES6中,不須要再hackthis了,但你須要遵循如下規則:

  • 經過object.method()語法調用的方法使用非箭頭函數定義,這些函數須要從調用者的做用域中獲取一個有意義的this值。

  • 其它狀況全都使用箭頭函數。

// ES6
    {
      ...
      addAll: function addAll(pieces) {
        _.each(pieces, piece => this.add(piece));
      },
      ...
    }

在ES6的版本中,注意addAll方法從它的調用者處獲取了this值,內部函數是一個箭頭函數,因此它繼承了外圍做用域的this值。

超讚的是,在ES6中你能夠用更簡潔的方式編寫對象字面量中的方法,因此上面這段代碼能夠簡化成:

// ES6的方法語法
    {
      ...
      addAll(pieces) {
        _.each(pieces, piece => this.add(piece));
      },
      ...
    }

在方法和箭頭函數之間,我不再會錯寫functoin了,這真是一個絕妙的設計思想!

箭頭函數與非箭頭函數間還有一個細微的區別,箭頭函數不會獲取它們本身的arguments對象。誠然,在ES6中,你可能更多地會使用不定參數和默認參數值這些新特性。

相關文章
相關標籤/搜索