# What advantage is there for using the arrow syntax for a method in a constructor? ### Q:對建構式中的方法使用箭頭函式有什麼優點? Ans: 在建構式中使用箭頭函式作為方法的主要優點是 * 一般函式的this會依照執行時被呼叫的地方決定,箭頭函式會自動綁定 (bind) this 到宣告箭頭函式當時的環境 (context)。當用此建構式創建新物件時,this的值會關聯於(指向)該物件。 * 提升程式碼的可閱讀性 ```+ const Person = function (firstName) { this.firstName = firstName; this.sayName1 = function () { console.log(this.firstName); }; this.sayName2 = () => { console.log(this.firstName); }; }; const john = new Person('John'); const dave = new Person('Dave'); john.sayName1(); // John john.sayName2(); // John // The regular function can have its 'this' value changed, but the arrow function cannot john.sayName1.call(dave); // Dave (because "this" is now the dave object) john.sayName2.call(dave); // John john.sayName1.apply(dave); // Dave (because 'this' is now the dave object) john.sayName2.apply(dave); // John john.sayName1.bind(dave)(); // Dave (because 'this' is now the dave object) john.sayName2.bind(dave)(); // John var sayNameFromWindow1 = john.sayName1; sayNameFromWindow1(); // undefined (because 'this' is now the window object) var sayNameFromWindow2 = john.sayName2; sayNameFromWindow2(); // John ``` ## Arrow Functions 的 this 判斷原則 > MDN & W3Schools: > (1) In arrow functions, this retains the value of the enclosing lexical context's this. > (2) Arrow functions do not have their own this. > (3) In global code, it will be set to the global object. Arrow Functions 的 this 和傳統函數this的一個重大差異就是在語彙環境(lexical context)。 「語彙環境」,也就是程式碼的實際位置(全域或是函式內)/ 函式被宣告時所在的 scope。 * 傳統函數每次呼叫函數,都會建立一個新的函數執行環境 (Function Execution Context),然後建立一個新的 this 引用物件,指向當下的呼叫者。 ![](https://i.imgur.com/voIwg0J.png) * Arrow Functions 沒有 prototype (原型),所以箭頭函式本身不會有自己的 this 引用物件,呼叫 this 時,會沿用語彙環境外圍的 this。 * 箭頭函式的this在定義的時候繼承自外層第一個普通函式的this,若外層沒有普通函式,則this指向Global Context的Global 物件。 * 箭頭函式本身的this指向不能改變,但可以修改它要繼承的物件的this * 由於箭頭函式看的是語彙位置,往外一層是 Global Context,不管在一般模式或嚴謹模式,this 都是 Global 物件: ```+ var whatsThis = () => { return this; } console.log( whatsThis() ); // window ``` ## 小試身手 ```+ //01 傳統函數 var x = 10; var obj = { x: 20, f: function(){ console.log('Output#1: ', this.x); var foo = function(){ console.log('Output#2: ', this.x); } foo(); } }; obj.f(); //02 Arrow Functions I //內部函數是 Arrow Function,外部函數仍是傳統函數: var x = 10; var obj = { x: 20, f: function(){ console.log('Output#1: ', this.x); var foo = () => { console.log('Output#2: ', this.x); } foo(); } }; obj.f(); //03 Arrow Functions II //外部函數是 Arrow Function,內部函數是傳統函數: var x = 10; var obj = { x: 20, f: () => { console.log('Output#1: ', this.x); var foo = function() { console.log('Output#2: ', this.x); } foo(); } }; obj.f(); //04 Arrow Functions III //外部函數和內部函數都是 Arrow Function: var x = 10; var obj = { x: 20, f: () => { console.log('Output#1: ', this.x); var foo = () => { console.log('Output#2: ', this.x); } foo(); } }; obj.f(); ``` ```+ //解答 //(1) Output#1: 20 Output#2: 10 //(2) Output#1: 20 Output#2: 20 //(3) Output#1: 10 Output#2: 10 //(4) Output#1: 10 Output#2: 10 ``` ## 不可使用箭頭函式的情況 * **物件中的方法** 箭頭函式會以定義當下的this值為this值=window物件,所以是存取不到物件中的this.array值的。 ```+ const calculate = { array: [1, 2, 3], sum: () => { return this.array.reduce((result, item) => result + item) ////this指向 window Object } } //TypeError: Cannot read property 'array' of undefined calculate.sum() ``` * **Prototype 中使用 this** 一樣是 this 的問題,如果原型上新增一個箭頭函式,並嘗試使用 this 的話會指向全域。 ```+ function MyCat(name) { this.catName = name; } MyCat.prototype.sayCatName = () => { return this.catName; //this指向 window Object }; MyCat.prototype.sayCatName2 = function () { return this.catName; }; cat = new MyCat("Mew"); console.log(cat.sayCatName()); //undefined console.log(cat.sayCatName2()); //Mew ``` * **DOM 事件監聽** ```+ const button = document.getElementById('myButton') button.addEventListener('click', () => { this.innerHTML = 'Clicked button' //this指向 window Object }) //TypeError: Cannot set property 'innerHTML' of undefined ``` * **建構式** 由於 this 是在物件下建立,所以箭頭函式不能像 function 一樣作為建構式的函式,如果嘗試使用此方法則會出現錯誤 (... is not a constructor)。 ```+ const Message = (text) => { this.text = text; } // Throws "TypeError: Message is not a constructor" const helloMessage = new Message('Hello World!'); ``` ## 小結 傳統函式: this值是動態的,由呼叫這個函式的擁有者物件(Owner)決定 箭頭函式: this是lexical作用域決定,也就是由週邊的作用域所決定 ## 參考資料: [mdn箭頭函式](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Functions/Arrow_functions) https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/arrow_function.html https://something-about-js-book.onejar99.com/day21 https://wcc723.github.io/javascript/2017/12/21/javascript-es6-arrow-function/