# Arrow Function 箭頭函式 & 內部的 this Arrow function expression 可以說是原型function expression的一個簡潔寫法的替代方案,但是在使用上有一些限制和語法上的不同。 * 箭頭函式不具備自己的'this', 'arguments' 或 'super' 綁定,而且不適合用作於物件的方法 * 箭頭函式不可以使用 Constructors,如果使用 new 方法呼叫他們,會拋出 TypeError 的錯誤。 * 箭頭函式無法在主體內使用 "yield",並且也無法使用 generator function。 ## 箭頭函式的基本語法 const sum = function(a,b) => a + b ## 不具備自己的 'this', 'arguments' 或 'super' 綁定 ```javascript= // 箭頭函式的基本語法 const sum = function(a,b) => a + b // 不具備自己的 this 綁定 const obj = { value: 10 getValue: () => { // 在箭頭函式內的 this 會指向外部的作用域,而不是 obj return this.value } } console.log(obj.getValue()) // 在這裡 this.value 會是 undefined,因為箭頭函式內並沒有自己的this 綁定 ``` ```javascript= // 不具備自己的 arguments 綁定 const printArguments = () => { // 箭頭函式內的 arguments 會參考到作用域外部的 arguements // 而不是函數自己的 arguments console.log(arguments) } // 此處會得到 Uncaught ReferenceError: arguments is not defined printArguments(1,2,3) ``` ```javascript= // 不具備自己的 super 綁定 class Parent{ constructor(){ this.name = "Parent" } printName = () => { // 箭頭函數內部沒有自己的 super綁定,所以無法使用 super console.log(this.name) } } const child = new Parent() child.printName() // 箭頭函式內的 this 就會指向外部作用域的 this,會正確印出 "Parent" ``` [MDN super的用法](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/super) ## 異步函數內使用的箭頭函式 **使用一般函式和箭頭函式方法的比對** **一般函式的用法:** 這裡的 function 會綁定自己的 this,當這些函數被作為回乎傳遞給 fetch 的 "then" 方法時,可能會不正確,因為它會取決於被呼叫的上下文。 **箭頭函式的用法:** 不具備自己的 "this" 綁定,而是繼承外部作用域的 "this",在這裡就會是跟 fetchData 函數的 "this" 一致,這使得箭頭函式內部的 "this" 就會自然而然地保留外部作用域的 "this",而不會受到 **.then 方法內部的綁定問題**的影響。 ```javascript= // 一般函式 function fetchData(){ fetch('https://api.example.com/data') .then(function(response){ // 這裡的 this 可能會有問題,因為是來自於 fetch 回呼函式產生的,這裡的 function 會綁定自己的 this。 return(response.json()) }) .then(function(data){ // 同樣的問題也會發生在這裡 console.log(data) }) } // 箭頭函式的用法 function fetchData(){ fetch('https://api.example.com/data') .then(response => response.json()) .then(data => console.log(data)) } ``` ## 重新認識 "this" & 箭頭函式內部的 "this" 用法 this 是 object 正在執行 function 的一個參考值 1. method -> obj 如果 function 是一個物件的方法,那 "this" 指向的就是 obj 2. function -> global (window, global) ```javascript= const video = { titel: "a", play(){ console.log(this) } } video.stop = function(){ console.log(this) } video.play() video.stop() function playVideo(){ console.log(this) } // normal function 印出來就會是 WINDOW OBJECT playVideo() // constructor function function Video(title){ this.title = title console.log(this) } const v = new Video("b") // 在這裡 new 會 create 一個空物件 {},並將 title 帶入。 ``` video.play() 和 video.stop() 都會指向 video obj ![image](https://hackmd.io/_uploads/HJZGT15i6.png) 若是普通的function: playVideo 函式印出來的 this,就會是 window object。 ![image](https://hackmd.io/_uploads/BJXtaJ9i6.png) constructor function Video 印出來的 this ![image](https://hackmd.io/_uploads/HJ2N21qi6.png) function 內部不同的 this 指向問題: ```javascript= const video = { title: "a", tags: ["a", "b", "c"], showTags(){ // 在 showTags method 裡指向 video obj ,抓取 tags 屬性的值,使用 forEach 方法。 // forEach 方法若如下使用普通 function,會有自己的this,在這裡就會顯示錯誤,因為這裡 this 指向的就是 window object。 this.tags.forEach(function(tag){ console.log(this.title, tag) }) } } // 這裡解決 forEach 內部 console.log 出現 undefined 的問題其中兩個方式: // 1. 加入 forEach 的第二個參數 thisArg forEach(callbackFn, thisArg) // 在 showTags 方法內部,可以在 forEach 的第二個參數放入 "this" this.tags.forEach(function(tag){ console.log(this.title, tag) }, this) // 2. 直接改成箭頭函式: 直接抓取外部作用域的 "this",也就是 video 的 obj this.tags.forEach((tag) => console.log(this.title, tag)) ``` 使用一般函式內部印出來的 this.title 產生的問題,小心就會變成 undefined 錯誤 ![image](https://hackmd.io/_uploads/SkZkEx5sp.png) ## 參考資料 * [MDN Arrow Function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions) * [JavaScript this Keyword](https://www.youtube.com/watch?v=gvicrj31JOM)