箭頭函式基本架構
1 | // 正常結構 |
延伸閱讀:重讀 Axel 的 Javascript 中的 Expression vs Statement 一文
傳統函式與箭頭函式寫法差異
1 | // 傳統函式 |
箭頭函式 - 縮寫
只可用在 function code 只有一行的情況。
1 | // 1. "省略大括號 + return"。 |
註:雖然很多情況可以省略圓括號(),但建議你一律都用圓括號框起來。(Google 5.5.3, eslint: arrow-parens)
補充
1 | // 若返回值或參數有包含{},像是物件內容,需要在外圍加上括號() |
箭頭函式沒有 Arguments 物件
傳統函式
傳統函式在執行環境被創造的時候,JavaScript 引擎會自動生成一個 arguments
物件。
1 | const foo = function() { |
箭頭函式
ES6 的箭頭函式不會產生Arguments類物件
,若要取用參數,需要利用Spread - 展開與其餘參數這篇所提到的其餘參數
方法來代替。
1 | // 錯誤示範 |
1 | // ES6其餘參數 |
This 綁定差異
參考資料
傳統函式 | 箭頭函式 |
---|---|
this 值是動態的,由呼叫這個函式的物件(Owner)決定 |
this 是由箭頭函式本身所在的詞彙環境所決定,也就是他所在的函式作用域(function scope)的this 就是他的this ,其他塊級作用域(block scope/if、for..等的)不會影響他的this |
在 ES6 之後,JS 引進了一種新式的設計,稱為 Lexical this(詞彙環境的 this),下面簡單的說明一下。
以傳統函式而言,在 JS 中的預設 this 值,就是呼叫這個函式的物件,例如是 obj.method(),除非 method 是一個綁定方法(一般指 bind 這個方法),不然 this 預設就是 obj。
箭頭函式的預設 this 就是用 Lexical this(詞彙環境的 this),它雖然是函式,但它不像傳統函式的設計,在被呼叫時以呼叫它的物件作為預設 this 值,或是在全域呼叫時以 window 或全域物件作為預設 this (或是嚴格模式下是 undefined )。它不是這樣設計的,它是從詞彙環境(Lexical enviroment)中捕捉 this 值,作為自己的預設 this 值。
★ 會影響 Lexical this( 詞彙環境的 this )的只有函數的作用域(function scope),包含函式或全域的作用域。其他的塊級作用域(block scope,例如 if、 for..等等)並不會影響箭頭函式的 this。
上面講了一堆,實際看例子就會很容易理解,這是一個在函式中使用箭頭函式的例子:
更多例子 - 箭頭函數的 this
1 | const obj = {} |
在以往這個有名的常見問題中,解決方式有很多種,要不就是要開發者自己作”捕捉”外層函式 this 的動作,或是用 bind 方法自己綁定,下面這是其中一種用”捕捉”外層函式 this 的解決方式:
1 | const obj = {} |
類似的問題非常多,你可以從 setTimeout 的例子就可以看到,這種用了 callback(回調, 回呼)作為傳入參數的方法,在 JS 中多到一個程度,語法也是很常見,可見在真實的應用情況,如果能像箭頭函式能作週邊的捕抓 this 值,作為自己的預設 this,可以得到多大的方便與減少多少的濳在問題。
當然,箭頭函式的 Lexical this(詞彙的 this)作用在大部的情況都可以運作得很好,但它也有在某些下不適合使用的情況,因為 Lexical this(詞彙的 this)一旦綁定過了,就無法再覆蓋,即使是用 new 關鍵字也不行。不過,這部份可能會比較進階,你可以參考 You Don’t Know JS: this & Object Prototypes 與這篇文章中的內容。也因為如此,有些情況下你不應該使用箭頭函式,在下面的內容中會說明。
不可使用箭頭函式的情況
物件中的方法
因為箭頭函式會以物件在定義時的捕捉到的週邊 this 為預設 this,下例的箭頭函式並沒有函數包覆,而他只受function scope影響,因此他會離開 obj 物件的 scope,往外找到全域環境,我們都知道在全域環境執行 console.log(this)
會得到 window,這也成為此例箭頭函式捕捉到的 this 參考,所以此例的 this 指向的是 window(或是在嚴格模式的 undefined)。
1 | const obj = { |
不可搭配 apply
, call
, bind
this
在 Arrow function 中是被綁定的(參考周邊函數的 this 的),所以套用 call 的方法時是無法修改 this 的。
1 | let family = { |
不能當作建構子
由於 this
的是在物件下建立,所以箭頭函式不能像 function 一樣作為建構式的函式,如果嘗試使用此方法則會出現錯誤 (... is not a constructor)
。
1 | var Construcor = () => { |
DOM 事件監聽
同先前說的, this
是指向所建立的物件上,如果是用在監聽 DOM 上一樣會指向 window
,所以無法使用在此情境。
1 | var el = document.getElementsByTagName('div'); |
Prototype 中使用 this
一樣是 this
的問題,如果原型上新增一個箭頭函式,並嘗試使用 this
的話會指向全域。
1 | function construc() { |