add by zhj: 最近在看ES6,看到了箭頭函數,我我的感受箭頭函數適用於函數體中不用this的匿名函數,在箭頭函數中使用this是一個坑node
原文:http://ourjs.com/detail/584f83664edfe07ccdb23445程序員
在ES6大行其道的今天,不該用點ES6特性彷佛有些政治不正確。最近恰好有個Node的項目,最低要支持到nodejs 4.0,在node.green看了下ES6的支持度,我想使用的特性基本都有支持,遂決定在新項目中採用ES6來寫。編程
固然第一件事情就是絕不留情地消滅var,項目中能用const的地方不用let,能用let的地方不用var。閉包
第二件事情就是使用勞動人民喜聞樂見的箭頭函數替代function。當我心滿意足地看到滿屏的=>
時,現實給了我一記響亮的耳光——改過以後的程序錯誤百出!編程語言
因此,當咱們使用箭頭函數時,必定要搞清楚箭頭函數是什麼回事,適用於什麼場景。本文就針對以上問題來討論下箭頭函數。函數式編程
箭頭函數的語法我就不講了,相信你們都見識過。跟我同樣,你們喜歡箭頭函數90%的緣由是它好看。除了好看,它是否是與function等價呢?確定不等價,由於TC39不可能僅由於好看而引入一個語法糖(class除外)。函數
箭頭函數的淵源能夠追溯到上古時期一個叫lambda演算的東西。lambda演算是數學家提出來的,有些數學家跟咱們程序員同樣也很懶,數學定理那麼多,今天要證三角定律,明天要證勾股定律,累不累!那能不能將全部的證實問題用一個統一的體系進行形式化描述,而後由機器來完成自動推導呢?lambda演算就是幹這個的,圖靈也搞了一套體系叫圖靈機,二者是等價的。this
關於lambda演算說了這麼多,好像跟今天要講的箭頭函數沒什麼關係?實際上是有關係的,lambda演算深入影響了箭頭函數的設計。數學家們喜歡用純函數式編程語言,純函數的特色是沒有反作用,給予特定的輸入,老是產生肯定的輸出,甚至有些狀況下經過輸出可以反推輸入。要實現純函數,必須使函數的執行過程不依賴於任何外部狀態,整個函數就像一個數學公式,給定一套輸入參數,不論是在地球上仍是火星上執行都是同一個結果。spa
箭頭函數要實現相似純函數的效果,必須剔除外部狀態。因此當你定義一個箭頭函數,在普通函數裏常見的this
、arguments
、caller
是通通沒有的。prototype
this
箭頭函數沒有this
,那下面的代碼明顯能夠取到this
啊:
function foo() { this.a = 1 let b = () => console.log(this.a) b() } foo() // 1
以上箭頭函數中的this
實際上是父級做用域中的this
,即函數foo
的this
。箭頭函數引用了父級的變量,構成了一個閉包。以上代碼等價於:
function foo() { this.a = 1 let self = this let b = () => console.log(self.a) b() } foo() // 1
箭頭函數不只沒有this
,經常使用的arguments
也沒有。若是你能獲取到arguments
,那它必定是來自父做用域的。
function foo() { return () => console.log(arguments[0]) } foo(1, 2)(3, 4) // 1
上例中若是箭頭函數有arguments
,就應該輸出的是3而不是1。
一個常常犯的錯誤是使用箭頭函數定義對象的方法,如:
let a = { foo: 1, bar: () => console.log(this.foo) } a.bar() //undefined
以上代碼中,箭頭函數中的this
並非指向a
這個對象。對象a
並不能構成一個做用域,因此再往上到達全局做用域,this
就指向全局做用域。若是咱們使用普通函數的定義方法,輸出結果就符合預期,這是由於a.bar()
函數執行時做用域綁定到了a
對象。
let a = { foo: 1, bar: function() { console.log(this.foo) } } a.bar() // 1
另外一個錯誤是在原型上使用箭頭函數,如:
function A() { this.foo = 1 } A.prototype.bar = () => console.log(this.foo) let a = new A() a.bar() //undefined
一樣,箭頭函數中的this
不是指向A
,而是根據變量查找規則回溯到了全局做用域。一樣,使用普通函數就不存在問題。
經過以上說明,咱們能夠看出,箭頭函數除了傳入的參數以外,真的是什麼都沒有!若是你在箭頭函數引用了this
、arguments
或者參數以外的變量,那它們必定不是箭頭函數自己包含的,而是從父級做用域繼承的。
到這裏,咱們能夠發現箭頭函數並非萬金油,稍不留神就會踩坑。
至於什麼狀況該使用箭頭函數,《You Don’t Know JS》給出了一個決策圖:
以上決策圖看起來有點複雜,我認爲有三點比較重要:
map
、reduce
、filter
的回調函數定義中;this
會很容易污染全局做用域。最起碼在箭頭函數外部包一層普通函數,將this
控制在可見的範圍內;