深入淺出ES6:箭頭函數(shù)

箭頭符號在JavaScript誕生時就已經(jīng)存在,當初第一個JavaScript教程曾建議在HTML注釋內(nèi)包裹行內(nèi)腳本噪珊,這樣可以避免不支持JS的瀏覽器誤將JS代碼顯示為文本晌缘。你會寫這樣的代碼:

<scriptlanguage="javascript">

<!--

? ?document.bgColor = "brown";? // red

? ?// -->

</script>

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

為了支持這種奇怪的hack方式磷箕,瀏覽器中的JavaScript引擎將標簽后的首行,在JS代碼的每個角落你都有可能見到它阵难,甚至在Node中也是如此岳枷。-->

碰巧,這種注釋風格首次在ES6中被標準化了,但在新標準中箭頭被用來做其它事情空繁。

箭頭序列-->同樣是單行注釋的一部分殿衰。古怪的是,在HTML中-->之前的字符是注釋的一部分盛泡,而在JS中-->之后的部分才是注釋闷祥。

你一定感到陌生的是,只有當箭頭在行首時才會注釋當前行傲诵。這是因為在其它上下文中凯砍,-->是一個JS運算符:“趨向于”運算符!

function countdown(n) {

while(n-->0) ? ? // "n goes to zero"

alert(n);

blastoff();

}

上面這段代碼可以正常運行拴竹,循環(huán)會一直重復直到n趨于0悟衩,這當然不是ES6中的新特性,它只不過是將兩個你早已熟悉的特性通過一些誤導性的手段結合在一起栓拜。你能理解么座泳?通常來說,類似這種謎團都可以在Stack Overflow上找到答案菱属。

當然钳榨,同樣地,小于等于操作符<=也形似箭頭纽门,你可以在JS代碼、隱藏的圖片樣式中找到更多類似的箭頭营罢,但是我們就不繼續(xù)尋找了赏陵,你應該注意到我們漏掉了一種特殊的箭頭。

? <!-- 單行注釋

--> “趨向于”操作符

<= 小于等于

=> 這又是什么饲漾?

=> 到底是什么蝙搔?我們今天就來一探究竟。

首先考传,我們談論一些有關函數(shù)的事情吃型。

函數(shù)表達式無處不在

JavaScript中有一個有趣的特性,無論何時僚楞,當你需要一個函數(shù)時勤晚,你都可以在想添加的地方輸入這個函數(shù)。

舉個例子泉褐,假設你嘗試告訴瀏覽器用戶點擊一個特定按鈕后的行為赐写,你會這樣寫:

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

jQuery的.click()方法接受一個參數(shù):一個函數(shù)。沒問題膜赃,你可以在這里輸入一個函數(shù):

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

對 于現(xiàn)在的我們來說挺邀,寫出這樣的代碼相當自然,而回憶起在這種編程方式流行之前,這種寫法相對陌生一些端铛,許多語言中都沒有這種特性泣矛。1958年,Lisp首 先支持函數(shù)表達式禾蚕,也支持調用lambda函數(shù)乳蓄,而C++,Python夕膀、C#以及Java在隨后的多年中一直不支持這樣的特性虚倒。

現(xiàn)在截然不同,所有的四種語言都已支持lambda函數(shù)产舞,更新出現(xiàn)的語言普遍都支持內(nèi)建的lambda函數(shù)魂奥。我們必須要感謝JavaScript和早期的JavaScript程序員,他們勇敢地構建了重度依賴lambda函數(shù)的庫易猫,讓這種特性被廣泛接受耻煤。

令人傷感的是,隨后在所有我提及的語言中准颓,只有JavaScript的lambda的語法最終變得冗長乏味哈蝇。

// 六種語言中的簡單函數(shù)示例

function(a){returna>0;}// JS

[](int a){returna>0;}// C++

(lambda(a)(>a0));;Lisp

lambda a:a>0# Python

a=>a>0// C#

a->a>0// Java

箭袋中的新羽

ES6中引入了一種編寫函數(shù)的新語法

// ES5

var selected=allJobs.filter(function(job){

returnjob.isSelected();

});

// ES6

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

當你只需要一個只有一個參數(shù)的簡單函數(shù)時,可以使用新標準中的箭頭函數(shù)攘已,它的語法非常簡單:標識符=>表達式炮赦。你無需輸入function和return,一些小括號样勃、大括號以及分號也可以省略吠勘。

(我個人對于這個特性非常感激,不再需要輸入function這幾個字符對我而言至關重要峡眶,因為我總是不可避免地錯誤寫成functoin剧防,然后我就不得不回過頭改正它。)

如果要寫一個接受多重參數(shù)(也可能沒有參數(shù)辫樱,或者是不定參數(shù)峭拘、默認參數(shù)、參數(shù)解構)的函數(shù)狮暑,你需要用小括號包裹參數(shù)list鸡挠。

// ES5

artotal=values.reduce(function(a,b){

returna+b;

},0);

// ES6

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

我認為這看起來酷斃了。

正如你使用類似Underscore.js和Immutable.js這樣的庫提供的函數(shù)工具心例,箭頭函數(shù)運行起來同樣美不可言宵凌。事實上,Immutable的文檔中的示例全都由ES6寫成止后,其中的許多特性已經(jīng)用上了箭頭函數(shù)瞎惫。

那么不是非常函數(shù)化的情況又如何呢溜腐?除表達式外,箭頭函數(shù)還可以包含一個塊語句瓜喇⊥σ妫回想一下我們之前的示例:

// ES5

$("#confetti-btn").click(function(event){

playTrumpet();

fireConfettiCannon();

});

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

// ES6

$("#confetti-btn").click(event=>{

playTrumpet();

fireConfettiCannon();

});

這是一個微小的改進,對于使用了Promises的代碼來說箭頭函數(shù)的效果可以變得更加戲劇性乘寒,}).then(function (result) {這樣的一行代碼可以堆積起來望众。

注意,使用了塊語句的箭頭函數(shù)不會自動返回值伞辛,你需要使用return語句將所需值返回烂翰。

小提示:當使用箭頭函數(shù)創(chuàng)建普通對象時,你總是需要將對象包裹在小括號里蚤氏。

// 為與你玩耍的每一個小狗創(chuàng)建一個新的空對象

var chewToys=puppies.map(puppy=>{});// 這樣寫會報Bug甘耿!

var chewToys=puppies.map(puppy=>({}));//

用小括號包裹空對象就可以了。

不幸的是竿滨,一個空對象{}和一個空的塊{}看起來完全一樣佳恬。ES6中的規(guī)則是,緊隨箭頭的{被解析為塊的開始于游,而不是對象的開始毁葱。因此,puppy => {}這段代碼就被解析為沒有任何行為并返回undefined的箭頭函數(shù)贰剥。

更令人困惑的是倾剿,你的JavaScript引擎會將類似{key: value}的對象字面量解析為一個包含標記語句的塊。幸運的是鸠澈,{是唯一一個有歧義的字符柱告,所以用小括號包裹對象字面量是唯一一個你需要牢記的小竅門。

這個函數(shù)的this值是什么呢笑陈?

普通function函數(shù)和箭頭函數(shù)的行為有一個微妙的區(qū)別,箭頭函數(shù)沒有它自己的this值葵袭,箭頭函數(shù)內(nèi)的this值繼承自外圍作用域涵妥。

在我們嘗試說明這個問題前,先一起回顧一下坡锡。

JavaScript中的this是如何工作的蓬网?它的值從哪里獲取鹉勒?這些問題的答案可都不簡單帆锋,如果你對此倍感清晰,一定因為你長時間以來一直在處理類似的問題禽额。

這個問題經(jīng)常出現(xiàn)的其中一個原因是锯厢,無論是否需要皮官,function函數(shù)總會自動接收一個this值。你是否寫過這樣的hack代碼:

{

...

addAll:functionaddAll(pieces){

varself=this;

_.each(pieces,function(piece){

self.add(piece);

});

},

...

}

在這里实辑,你希望在內(nèi)層函數(shù)里寫的是this.add(piece)捺氢,不幸的是,內(nèi)層函數(shù)并未從外層函數(shù)繼承this的值剪撬。在內(nèi)層函數(shù)里摄乒,this會是window或undefined,臨時變量self用來將外部的this值導入內(nèi)部函數(shù)残黑。(另一種方式是在內(nèi)部函數(shù)上執(zhí)行.bind(this)馍佑,兩種方法都不甚美觀。)

在ES6中梨水,不需要再hackthis了拭荤,但你需要遵循以下規(guī)則:

通過object.method()語法調用的方法使用非箭頭函數(shù)定義,這些函數(shù)需要從調用者的作用域中獲取一個有意義的this值冰木。

其它情況全都使用箭頭函數(shù)穷劈。

// ES6

{

...

addAll:functionaddAll(pieces){

_.each(pieces,piece=>this.add(piece));

},

...

}

在ES6的版本中,注意addAll方法從它的調用者處獲取了this值踊沸,內(nèi)部函數(shù)是一個箭頭函數(shù)歇终,所以它繼承了外圍作用域的this值。

超贊的是逼龟,在ES6中你可以用更簡潔的方式編寫對象字面量中的方法评凝,所以上面這段代碼可以簡化成:

// ES6的方法語法

{

...

addAll(pieces){

_.each(pieces,piece=>this.add(piece));

},

...

}

在方法和箭頭函數(shù)之間,我再也不會錯寫functoin了腺律,這真是一個絕妙的設計思想奕短!

箭頭函數(shù)與非箭頭函數(shù)間還有一個細微的區(qū)別,箭頭函數(shù)不會獲取它們自己的arguments對象匀钧。誠然翎碑,在ES6中,你可能更多地會使用不定參數(shù)和默認參數(shù)值這些新特性之斯。

借助箭頭函數(shù)洞悉計算機科學的風塵往事

我們已經(jīng)討論了許多箭頭函數(shù)的實際用例琅豆,它還有一種可能的使用方法:將ES6箭頭函數(shù)作為一個學習工具巫击,來深入挖掘計算的本質冻河,是否實用钝计,終將取決于你自己。

1936年瘫絮,Alonzo Church和Alan Turing各自開發(fā)了強大的計算數(shù)學模型涨冀,圖靈將他的模型稱為a-machines,但是每一個人都稱其為圖靈機麦萤。Church寫的是函數(shù)模型鹿鳖,他的模型被稱為lambda演算(λ-calculus)扁眯。這一成果也被Lisp借鑒,用LAMBDA來指示函數(shù)栓辜,這也是為何我們現(xiàn)在將函數(shù)表達式稱為lambda函數(shù)恋拍。

但什么是lambda演算呢?“計算模型”又意味著什么呢藕甩?

用 幾句話解釋清楚很難施敢,但是我會努力闡釋:lambda演算是第一代編程語言的一種形式,但畢竟存儲程序計算機在十幾二十年后才誕生狭莱,所以它原本不是為編程 語言設計的僵娃,而是為了表達任意你想到的計算問題設計的一種極度簡化的純數(shù)學思想的語言。Church希望用這個模型來證明普遍意義的計算腋妙。

最終他發(fā)現(xiàn)默怨,在他的系統(tǒng)中只需要一件東西:函數(shù)。

這種聲明方式無與倫比骤素,不借助對象匙睹、數(shù)組、數(shù)字济竹、if語句痕檬、while循環(huán)、分號送浊、賦值梦谜、邏輯運算符甚或是事件循環(huán),只須使用函數(shù)就可以從0開始重建JavaScript能實現(xiàn)的每一種計算袭景。

這是用Church的lambda標記寫出來的數(shù)學家風格的“程序”示例:

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

等效的JavaScript函數(shù)是這樣的:

varfix=f=>(x=>f(v=>x(x)(v)))

? ? ? ? ? ? ? ? (x=>f(v=>x(x)(v)));

所以唁桩,在JavaScript中實現(xiàn)了一個可以運行的lambda演算,它根植于這門語言中耸棒。

Alonzo Church和lambda演算后繼研究者們的故事荒澡,以及它是如何潛移默化地入駐每一門主流編程語言的,已經(jīng)遠超本文的討論范圍与殃。但是如果你對計算機科學 的奠基感興趣仰猖,或者你只是對一門只用函數(shù)就可以做許多類似循環(huán)和遞歸這樣的事情的語言倍感興趣,你可以在一個下雨的午后深入邱奇數(shù)(Church numerals)和不動點組合子(Fixed-point combinator)奈籽,在你的Firefox控制臺或Scratchpad中仔細研究一番。結合ES6的箭頭函數(shù)以及其它強大的功能鸵赫,JavaScript稱得上是一門探索lambda演算的最好的語言衣屏。

我何時可以使用箭頭函數(shù)?

早在2013年辩棒,我就在Firefox中實現(xiàn)了ES6箭頭函數(shù)的功能狼忱,Jan de Mooij為其優(yōu)化加快了執(zhí)行速度膨疏。感謝Tooru Fujisawa以及ziyunfei(譯者注:中國開發(fā)者,為Mozilla作了許多貢獻)后續(xù)打的補丁钻弄。

微軟Edge預覽版中也實現(xiàn)了箭頭函數(shù)的功能佃却,如果你想立即在你的Web項目中使用箭頭函數(shù),可以使用Babel窘俺、Traceur或TypeScript饲帅,這三個工具均已實現(xiàn)相關功能。


最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末瘤泪,一起剝皮案震驚了整個濱河市灶泵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌对途,老刑警劉巖赦邻,帶你破解...
    沈念sama閱讀 216,651評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異实檀,居然都是意外死亡惶洲,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評論 3 392
  • 文/潘曉璐 我一進店門膳犹,熙熙樓的掌柜王于貴愁眉苦臉地迎上來恬吕,“玉大人,你說我怎么就攤上這事镣奋”液牵” “怎么了?”我有些...
    開封第一講書人閱讀 162,931評論 0 353
  • 文/不壞的土叔 我叫張陵侨颈,是天一觀的道長余赢。 經(jīng)常有香客問我,道長哈垢,這世上最難降的妖魔是什么妻柒? 我笑而不...
    開封第一講書人閱讀 58,218評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮耘分,結果婚禮上举塔,老公的妹妹穿的比我還像新娘。我一直安慰自己求泰,他們只是感情好央渣,可當我...
    茶點故事閱讀 67,234評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著渴频,像睡著了一般芽丹。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卜朗,一...
    開封第一講書人閱讀 51,198評論 1 299
  • 那天拔第,我揣著相機與錄音咕村,去河邊找鬼。 笑死蚊俺,一個胖子當著我的面吹牛懈涛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播泳猬,決...
    沈念sama閱讀 40,084評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼批钠,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了暂殖?” 一聲冷哼從身側響起价匠,我...
    開封第一講書人閱讀 38,926評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎呛每,沒想到半個月后踩窖,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,341評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡晨横,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,563評論 2 333
  • 正文 我和宋清朗相戀三年洋腮,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片手形。...
    茶點故事閱讀 39,731評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡啥供,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出库糠,到底是詐尸還是另有隱情伙狐,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評論 5 343
  • 正文 年R本政府宣布瞬欧,位于F島的核電站贷屎,受9級特大地震影響,放射性物質發(fā)生泄漏艘虎。R本人自食惡果不足惜唉侄,卻給世界環(huán)境...
    茶點故事閱讀 41,036評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望野建。 院中可真熱鬧属划,春花似錦、人聲如沸候生。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽唯鸭。三九已至嗽测,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背唠粥。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留停做,地道東北人晤愧。 一個月前我還...
    沈念sama閱讀 47,743評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像蛉腌,于是被迫代替她去往敵國和親官份。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,629評論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1.函數(shù)參數(shù)的默認值 (1).基本用法 在ES6之前烙丛,不能直接為函數(shù)的參數(shù)指定默認值舅巷,只能采用變通的方法。
    趙然228閱讀 688評論 0 0
  • 原文鏈接:https://github.com/EasyKotlin 值就是函數(shù)河咽,函數(shù)就是值钠右。所有函數(shù)都消費函數(shù),...
    JackChen1024閱讀 5,970評論 1 17
  • 函數(shù)參數(shù)的默認值 基本用法 在ES6之前忘蟹,不能直接為函數(shù)的參數(shù)指定默認值飒房,只能采用變通的方法。 上面代碼檢查函數(shù)l...
    呼呼哥閱讀 3,382評論 0 1
  • 本文由我們團隊的 糾結倫 童鞋撰寫媚值。 寫在前面 本篇文章是對我一次組內(nèi)分享的整理狠毯,大部分圖片都是直接從keynot...
    知識小集閱讀 15,242評論 11 172
  • 寫在前面 本篇文章是對我一次組內(nèi)分享的整理,大部分圖片都是直接從keynote上截圖下來的褥芒,本來有很多炫酷動效的嚼松,...
    等開會閱讀 14,436評論 6 69