JS中lambda表達式的優缺點和使用場景(轉)

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

箭頭函數要實現相似純函數的效果,必須剔除外部狀態。因此當你定義一個箭頭函數,在普通函數裏常見的thisargumentscaller是通通沒有的。prototype

箭頭函數沒有this

箭頭函數沒有this,那下面的代碼明顯能夠取到this啊:

function foo() {
  this.a = 1
  let b = () => console.log(this.a)

  b()
}

foo()  // 1

以上箭頭函數中的this實際上是父級做用域中的this,即函數foothis。箭頭函數引用了父級的變量,構成了一個閉包。以上代碼等價於:

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,而是根據變量查找規則回溯到了全局做用域。一樣,使用普通函數就不存在問題。

經過以上說明,咱們能夠看出,箭頭函數除了傳入的參數以外,真的是什麼都沒有!若是你在箭頭函數引用了thisarguments或者參數以外的變量,那它們必定不是箭頭函數自己包含的,而是從父級做用域繼承的。

什麼狀況下該使用箭頭函數

到這裏,咱們能夠發現箭頭函數並非萬金油,稍不留神就會踩坑。

至於什麼狀況該使用箭頭函數,《You Don’t Know JS》給出了一個決策圖: arrow

以上決策圖看起來有點複雜,我認爲有三點比較重要:

  1. 箭頭函數適合於無複雜邏輯或者無反作用的純函數場景下,例如用在mapreducefilter的回調函數定義中;
  2. 不要在最外層定義箭頭函數,由於在函數內部操做this會很容易污染全局做用域。最起碼在箭頭函數外部包一層普通函數,將this控制在可見的範圍內;
  3. 如開頭所述,箭頭函數最吸引人的地方是簡潔。在有多層函數嵌套的狀況下,箭頭函數的簡潔性並無很大的提高,反而影響了函數的做用範圍的識別度,這種狀況不建議使用箭頭函數。
相關文章
相關標籤/搜索