Day 20:ES6 的箭頭函數 (Arrow Functions)
昨天的文章我們介紹到傳統 JavaScript 對於函數定義的語法有 4 種寫法。
懶人包支援: 1. 宣告式 (Function Declarations) 2. 匿名表達式 (Function Expressions w/o Function Name) 3. 具名表達式 (Function Expressions w/ Function Name) 4. 建構子式 (Function Constructor)
這 4 種寫法主要差異在於語法和 Hoisting 效果的差別,其餘少數極細微的差異對於函數的使用呼叫並不影響。換言之,同樣的函數內容,用哪一種寫法並不影響函數內的運作。
相較之下,Arrow Functions 就像函數定義的新種族。
箭頭函數 (Arrow functions) 是 ES6 具代表性的新特性之一,語法上的多項簡化是它的特色,因此很多剛接觸 ES6 未深的開發者會誤以為 Arrow Functions 只是函數定義語法的簡寫版。
事實上,Arrow Functions 所附帶的新特性對於函數內容的運作有著不同的行為,如果沒弄清楚,單純因為趕流行,把舊函數的語法簡化成 Arrow Functions 的寫法,可能造成程式運作不正確。
本篇文章就來介紹 Arrow Functions。
Arrow Functions 小檔案
ECMAScript 2015 (ES6) 導入的新特性。
稱為 Arrow Function Expression,或稱 Fat Arrow Functions,最初在 CoffeeScript 的語法中流行。
在函數定義的語法上更為簡潔。
函數運作行為上和傳統語法所定義的函數有差異,例如
arguments
、this
。
Arrow Functions 語法
1. 標準語法
以下是 Arrow Functions 的標準寫法:
和傳統語法比起來:
少了
function
關鍵字。使用
=>
符號來告知這是 Arrow Functions。
根據函數內容和參數數量,還可以進一步簡化。
2. 當函數內容只有單行回傳時的簡寫
很常我們的函數內容只做很簡單的動作,就像前面的 add()
只有一行負責簡單運算並同時做 return
。語法上可以進一步簡化:
省略函數外殼
{
}
。省略
return
關鍵字。
3. 當只有單一個參數時的簡寫
例如以下範例,intro()
只有一個參數 name
:
intro()
在參數部分的語法可以進一步簡化,省略小括號,效果一樣:
搭配前面提到的單行回傳簡寫法,整體語法就更顯簡潔:
但記得這是「單一個參數」時的簡寫,若是沒有參數或多個參數,仍必須用標準寫法,例如以下是沒有參數的範例:
Arrow Functions 使用的注意事項
1. 沒有 Hoisting 效果
Arrow Functions 是表達式的語法形式,就像前面介紹過的傳統函數中「具名表達式」或「匿名表達式」那樣,函數定義的部分不會被 Hoist。
換言之,定義必須寫在使用之前。
2. 建議使用 const
做名稱部分的宣告
const
做名稱部分的宣告雖然前面例子故意都用 var
,但實際上建議使用 const
。
因為函數表達式應該被視為一個常數的值,而非變數,函數定義這件事並不是一個該變動的東西。
W3Schools: A function expression is always constant value.
Arrow Functions 和傳統函數語法的差異
Arrow Functions 不是單純語法簡化而已,也會對函數的運作行為多了些限制或改變。
1. 不會產生新的 arguments
物件
arguments
物件筆者更新:這裡原本寫的是「不能使用
arguments
」,後來發現這個理解並不精確,因為如果外圍包裝一層傳統函數,作用域內還是會有可用的arguments
,因此進行修訂。
Arrow Functions 不會在自己的函數作用域內產生新的 arguments
物件,讓函數使用上更嚴謹。
例如以下是傳統函數寫法,雖然在函數定義的上只定義了 n1
和 n2
兩個參數,但實際上呼叫函數時我可以丟任意個參數進去,而在函數內也可以靠 arguments
取得所有參數:
甚至極端一點,我可以完全不管參數定義什麼:
這讓函數的參數定義非常沒有約束力。看起來好像很自由,但這種寫法很容易導致「包裝與內容物不符合」,會讓程式難以維護。
而 Arrow Functions 改善這一點。在 Arrow Functions 內不會為這個作用域建立一個新的 Arguments
物件,因此 arguments
不再被認得:
無法使用 arguments
,代表只能取用被定義的參數。雖然依舊無法阻止呼叫端任意亂丟參數進來,但至少會讓函數內容更可控。
但要注意,Arrow Functions 只是不會產生新的 arguments
,不代表 arguments
變數一定不存在。
例如以下例子,使用一個傳統函數包裝一個 Arrow Function:
傳統函數
getObj()
還是會產生新的arguments
。對作用域來說,
f()
可以使用getObj()
內存在的變數。因此如果在
f()
內去使用arguments
,會取到的是getObj()
的arguments
,要特別注意。
如果使用 Arrow Function 去包裝另一個 Arrow Function:
getObj()
和f()
都不會產生新的arguments
。因此在
getObj()
和f()
內企圖使用arguments
,都會得到arguments is not defined
的錯誤。
2. this
運作行為的不同
this
運作行為的不同前面介紹 this
時,提到判斷 this
代表什麼物件的大原則:看呼叫時的物件是誰。
例如借用函數的例子 (函數被定義在物件之外),雖然 whatsThis
語彙上定義的地方是在 Global Context,但被執行時的呼叫者是 player
,因此回傳的 this
物件是 player
:
但如果是 Arrow Functions 就不一樣了:
可以發現,同樣的呼叫方式,this
不再回傳呼叫者物件,在這個例子變成回傳 Global 物件,是不同的運作行為。
Arrow Functions 的 this
究竟怎麼回事,我們會在明天的文章進行介紹。
小結
Arrow Functions 重點小結:
是 ES6 導入的新特性。
在語法上更為簡潔。
不是單純語法簡化而已,也會對函數的運作行為多了些限制或改變:
函數執行時不會產生新的
arguments
物件。this
的運作方式與傳統函數不同。
定義的語法必須在使用之前 (不具 Hoisting 效果)。
建議使用
const
宣告名稱。
References
Last updated