# 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 引用物件,指向當下的呼叫者。

* 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/