在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ 箭頭函數
生成器
箭頭函數
繼續(xù)迭代器
未來前景
簡介
非結構化賦值
迭代器和 for-of 循環(huán)
代理
符號
模版字符串
let 和 const
容器
模塊

箭頭函數

箭頭函數( Arrow Function )很早就被應用在 JavaScript 中。第一版 JavaScript 教程中建議包裝腳本并內嵌在 HTML 解釋中。這將會阻止瀏覽器不支持 JS 時,以文本形式錯誤地顯示你的 JS 代碼。你最好這樣寫:

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

老的瀏覽器會認為上面的代碼是兩種不支持的標簽和解釋;只有新的瀏覽器會認為是 JS 代碼。

在你的瀏覽器中 JavaScript 引擎認為字符 <!-- 作為注釋的開始。不開玩笑地說,這真的是語言的一部分,并且直到現在,不僅僅是內聯(lián)的頂部 <script> ,整個 JS 代碼任意部分都屬于語言的一部分。它甚至可以在節(jié)點處被執(zhí)行。

湊巧的是,這種注釋的風格是 ES6 首次標準化。但這不是我們談論的箭頭函數。

箭頭序列 --> 表示單行注釋。奇怪地是,在 HTML 中在字符串 --> 前面是注釋部分,而在 JS 中字符串 --> 后面的部分表示注釋。

令人感到奇怪的是,這個箭頭只有當它出現在一行的開始才表示注釋。這是因為在內容的其它部分, --> 表示 JS 操作符, 表示 ”跳轉“操作符。

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

這段代碼真的會工作。循環(huán)運行直到 n 變?yōu)?0 。 這也不是 ES6 的新特性,但是一個常見特征的組合。你能想象發(fā)生了什么嗎?像往常一樣,這個謎題的答案可以找到堆棧溢出。

當然,這里也有小于或等于操作符 <=。 或許你還可以在 JS代碼中發(fā)現更多的箭頭函數。讓我們停下來觀察這些箭頭函數,發(fā)現有個箭頭函數消失了。

箭頭 說明
<!-- 單行注釋
--> ”跳轉”操作
<= 小于或等于操作符
=> ???

=>發(fā)生了什么呢?今天,讓我們來尋找它。

首先,讓我們來討論一些功能。

無處不在的函數表達式

JavaScript 的一個有趣特性是,任何時候你需要一個函數,你可以在函數右邊加入運行代碼。

舉個例子,假設你試圖告訴瀏覽器當用戶點擊一個特殊按鈕后怎么處理。你可以輸入這樣的代碼:

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

jQuery’s .click( ) 方法需要一個參數:一個函數。毫無問題,你可以在函數右邊這樣輸入:

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

像上面方式來寫代碼對于我們來說很自然。所以很好奇在 JavaScript 流行以前,很多語言都還不具備這種編程風格。當然 在 1958 年時,Lisp 語言已經有了函數表達式,通常也被稱為 lambda 函數。 但是像 C++, Python, C# 和Java 存在很多年了都還沒有具備這種編程風格。

至少現在上述四種語言已經具備了 lambda 函數。較新的語言通常都已經內置有l(wèi)ambda 函數了。我們能有現在的 JavaScript 應該感謝早起的 JavaScript 開發(fā)者,他們創(chuàng)建了依賴于 lambdas 函數的函數庫,導致這一特征被廣泛采用。

這里有點略帶憂傷,在我提及的所有語言中, JavaScript 關于 lambdas 函數的介紹稍微有點冗長。

// A very simple function in six languages.
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

一個新的箭頭函數

ES6 提供了新的語法規(guī)則來描述函數。

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

// ES6
var selected = allJobs.filter(job => job.isSelected());

當你僅僅只需要帶有一個參數的函數,這個新的箭頭函數語法簡單地描述為:標識符 => 表達式 ( Identifier => Expression ) 。你可以跳過編輯函數名和返回值,以及一些大括號,小括號和分號。

( 我個人非常感激這個特征。對于我來說,不用再次編輯函數很重要,因為我不可避免會輸錯函數名,這樣我不得不再回頭檢查并且改正。 )

在編寫一個帶有多個參數的函數時(或許沒有參數,或許有默認參數,或者是前面提到的非結構化參數),你需要在參數列表的外面加一層括號。

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

// ES6
var total = values.reduce((a, b) => a + b, 0);

我認為這樣的書寫那個時候看起來很美觀。

箭頭函數就像是在函數庫上編寫一樣, 顯得美觀。實際上,Immutable’s documentation 很多例子都是用 ES6 來編寫的,因此里面的很多例子都使用到了箭頭函數。

箭頭函數內除了可以有表達式外,還可以有一段語句。回想早期的例子:

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

這里是用 ES6 寫的代碼:

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

注意到一個箭頭函數,用塊來描述的時候可能沒有自動返回一個值。我們可以用 return 聲明來返回。

這里有個注意事項當用箭頭函數來創(chuàng)建一個空對象時,要用括號括起來:

// create a new empty object for each puppy to play with
var chewToys = puppies.map(puppy => {});   // BUG!
var chewToys = puppies.map(puppy => ({})); // ok

不幸的是,一個空的對象 {} 和一個空塊 {} 看起來是一樣的。在 ES6 當中 { 緊跟在箭頭函數后面表示的是一個塊的開始,而不是一個對象的開始。這樣代碼 puppy => {} 表示的是一個箭頭函數,什么都不做,空操作。返回一個未定義。

更令人困惑的是,像 {key: value} 這樣的對象聲明看起來像是一個包含有標簽的塊聲明。至少對你的 JavaScript 引擎來說是這樣的。幸運地是,你可以用括號將 { 這個歧義字符括起來。

什么是 this 指針?

普通的函數和箭頭函數還是存在細微的差別。箭頭函數沒有自己內部的 this 指針。在箭頭函數中, this 指針是繼承于其所在的作用域。

讓我們在實際使用的過程中來搞明白這到底是什么意思。

在JavaScript 中 this 指針是怎么工作的?它的值又是來自哪里?這里沒有簡潔的答案。如果你認為這是很簡單的,那是因為你對于 this 指針已經很熟悉了。

這一問題經常出現的一個原因是 function 函數自動獲得 this 指針,不管你想要與否。你曾經寫過這樣的代碼嗎?

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

這里你所寫的內部函數是 this.add(piece)。不幸地是,這個內部函數沒有繼承外部函數的 this 指針。這樣內部的函數用 this 將會返回未定義。這個臨時的變量 self 會調用外部的 this 到內部的函數來。( 另外一種方式在內部函數中調用 .bind(this) 。其他方式都不是很令人滿意。 )

在 ES6 中, this 指針的 hacks 代碼可以避免如果你遵循下面的規(guī)則:

  • 用非箭頭函數方法,將使用 object.method() 語法。這樣函數將會從調用者那里獲得一個有意義的 this 指針。
  • 用箭頭函數。
// ES6
{
  ...
  addAll: function addAll(pieces) {
    _.each(pieces, piece => this.add(piece));
  },
  ...
}

ES6 版本中,注意到 addAll 方法從調用者那里獲得一個 this 指針。上面內部的函數是一個箭頭函數,它從作用域處繼承 this 指針。

作為獎勵, ES6 通常提供了一個更短的方式來描述對象! 因此上面的代碼可以變得更簡潔:

// ES6 with method syntax
{
  ...
  addAll(pieces) {
    _.each(pieces, piece => this.add(piece));
  },
  ...
}

比較上面的兩種方式,可以發(fā)現用箭頭函數描述可以不再輸入 function 了。這是一個很好的想法。

普通函數和箭頭函數還有一個小差異,那就是箭頭函數不需要參數。當然, 在 ES6 中,你可能寧愿用使用其他參數或者默認參數。

箭頭函數在計算科學的發(fā)展史

我們這里討論一些關于箭頭函數的實際應用。這里我會討論一個用例: ES6 箭頭函數作為一個學習工具,來發(fā)現一些深層次的計算本質。不管實際與否,你可以自己來判斷。

早在 1936 年,Alan Church 和 Alan Turing 獨立開發(fā)了具有強大計算功能的數學模型。Turing 稱它的模型為一個機器,即其余人所稱道的圖靈機。Church 的模型被他稱為 λ- 演算。( λ 是一個希臘小寫字母 lambda 。 )這也是為什么 Lisp 語言也用 LAMBDA 單詞來表示函數的原因,這也是今天我們稱函數表達式為 “l(fā)ambdas” 的原因。

但是什么又是 λ- 演算呢?什么又是計算模型的思想呢?

這里很難用幾句話來解釋,但是這里是我的一些嘗試性解釋: λ- 演算是最早的編程語言。它的目的不是為了設計編程語言——在此之后,直到十年或到二十年后的樣子才有了存儲程序計算機,但是相當簡單,它只能執(zhí)行一些你想要的純粹數學表達式。 Church 想讓他的模型能夠證明一些概括性的計算。

并且他只需要一樣東西在他的系統(tǒng)里就是:函數。

他的想法是多么的奇怪。沒有對象,沒有數組,沒有數字,還沒有 if 聲明, while 循環(huán) , 分號和賦值,還沒有邏輯操作符,或者是事件,這對于 JavaScript 來說是可能的,只用函數來重新構建每種類型。

這里給出一個數學上的程序,用 Church’s 的 λ 表示法:

fix = λf.(λx.f(λv.x(x)(v)))(λx.f(λv.x(x)(v)))

與上面等價的 JavaScript 函數可以這樣來寫:

var fix = f => (x => f(v => x(x)(v)))
               (x => f(v => x(x)(v)));

也就是說, JavaScript 包含了一個實現 λ- 演算的部分。 即 λ- 演算在JavaScript 中。

關于 Alonzo Church 和后續(xù)的研究者關于 λ- 演算做的研究,以及它揭示了 λ- 演算如何成為大多數語言中的一部分,這些內容超出了這一章節(jié)的范圍。但是如果你對計算機科學的起源很感興趣,或者你對一個語言最開始只有函數沒有任何其他類型感興趣,你可以花費你雨天的中午來閱讀 Church numeralsfixed-point combinator,并且你還可以在你的 Firefox 控制臺上寫。由于 ES6 在箭頭函數方面的優(yōu)勢, JavaScript 可以合理地聲明自己的語言對于探索 λ- 演算是最好的語言。

我們什么時候能夠用箭頭函數?

ES6 的箭頭函數是由我于 2013 年在 Firefox 中實現。 Jan de Mooij 使得箭頭函數運行變得更快。感謝 Tooru Fujisawa 和 ziyunfei 提供補丁。

箭頭函數也在微軟的新版本中實現。他們也在 Babel,Traceur,和 TypeScript 得到實現,如果你對于在網上使用箭頭函數很感興趣。

在下一章節(jié)中我們又會討論 ES6 的一個奇怪用法。我們將看到 typeof x 會返回一個全新的值。我們將會發(fā)問:到底什么時候是一個名稱什么時候又是一個字符串呢?我們會努力思考這些問題。這些都令人感到新奇。所以請下周繼續(xù)加入我們來深度探索 ES6 關于符號的新特征。

下一篇:模塊