相信箭頭函式是很多前端開發者知道的 ES6 語法,不過有沒有想過為什麼會想用它?
而上篇介紹了 this 的指向,那箭頭函式調用 this 時的指向為何?
什麼時候又不建議使用箭頭函式?
這些問題將在這篇一一說明。
主要有兩點:
第一點還蠻好舉例的,就是箭頭函式的語法能讓函式更加精簡。
const addOne = function(num) {
return num++;
}
// 簡化後
const addOne = (num) => num++;
第二點將會在下個段落"箭頭函式調用 this"一併解說。
要在箭頭函式內知道 this 的指向,就是要看該箭頭函式被宣告時的作用域在哪裡。
所以像 foo 函式在全域被宣告,以下各種狀況 this 都會指向全域物件,都會印出 true。
const globalObject = this;
let foo = (() => this);
console.log(foo()); // window object
console.log(foo() === globalObject); // true
const obj = { foo: foo };
console.log(obj.foo() === globalObject); // true
console.log(foo.call(obj) === globalObject); // true
foo = foo.bind(obj);
console.log(foo() === globalObject); // true
這裡也拿上篇文章第 2 點的範例稍微改寫一下,此例的 showThis 函式也是屬於全域宣告,所以也印出全域物件。
const person = {
name: "Jay",
age: 24,
showThis: () => {
console.log(this);
}
};
person.showThis(); // window object
const showThisFunc = person.showThis;
showThisFunc(); // window object
如果將 showThis 函式調整一下,讓裡面箭頭函式的作用域是 showThis 函式,執行後印出的值就會是 person 物件。
const person = {
name: "Jay",
age: 24,
showThis() {
const showThisArrFunc = () => console.log(this);
return showThisArrFunc;
}
};
const showThisFunc = person.showThis();
showThisFunc(); // 印出 { name: 'Jay', age: 24 }
看完以上的兩個範例,我們得知箭頭函式的 this 是由它出現地方的作用域決定。
而在過去的開發經驗中,我們看過用 const that = this;
儲存 this 值 或是用 bind 等函式去綁定 this 等的情況,例如以下範例:
function usesThat(name) {
const that = this;
console.log(this); // usesThat 物件 {myName: 'Dave'}
this.myName = name;
function returnMe() {
console.log(this); // window 物件
return that;
}
return { returnMe };
}
const usesthat = new usesThat('Dave');
console.log(usesthat.returnMe().myName); // 'Dave'
這個範例中,巢狀函式 returnMe 內部的 this 指向全域,透過 that 儲存外部的 this 值,所以最後的 console.log
才能順利的印出 Dave
。
接著將 returnMe 改成箭頭函式,根據箭頭函式對 this 的特性,this 為 usesThat 物件,所以也能印出 Dave
。
function usesThat(name) {
console.log(this); // usesThat 物件 {myName: 'Dave'}
this.myName = name;
const returnMe = () => {
console.log(this); // usesThat 物件 {myName: 'Dave'}
return this;
}
return { returnMe };
}
const usesthat = new usesThat('Dave');
console.log(usesthat.returnMe().myName); // 'Dave'
由上面的範例可以知道這也是使用箭頭函式的一個時機,另外上篇文章的 Callback function & this 範例也有使用到箭頭函式,有興趣的讀者也可以回去翻來看。
不過箭頭函式也是有些不建議使用的地方,接著看下個段落。
如範例的 showNameArrowFunc 會往全域去找 dogName 屬性,當然找不到。
const myDog = {
dogName: 'puppy',
showNameArrowFunc: () => console.log(this.dogName),
showName() {
console.log(this.dogName);
}
}
myDog.showNameArrowFunc(); // undefined
myDog.showName(); // puppy
即使使用了 call,introArrowFunc 還是沒有改變 this 指向:
也就是 call、apply、bind 三個方法,無法"覆蓋/修改"箭頭函式中的 this 值
const myDog = {
dogName: 'puppy'
}
const intro = function(introContent) {
return `${introContent} ${this.dogName}`;
}
const result = intro.call(myDog, 'My dog name is');
console.log(result); // My dog name is puppy
const introArrowFunc = (introContent) => `${introContent} ${this.dogName}`;
const result2 = introArrowFunc.call(myDog, 'My dog name is');
console.log(result2); // My dog name is undefined
不會和 new 搭配使用,如下範例就出錯了。
const Foo = () => {};
const foo = new Foo(); // TypeError: Foo is not a constructor
而且箭頭函式本身也不會有 prototype,請看下面範例:
function giveLydiaPizza() {
return "Here is pizza!"
}
const giveLydiaChocolate = () => "Here's chocolate... now go hit the gym already."
console.log(giveLydiaPizza.prototype)
console.log(giveLydiaChocolate.prototype)
答案: { constructor: ...} undefined
原因:
giveLydiaPizza()
有 prototype 原型屬性,為 constructor 物件giveLydiaChocolate()
,則沒有 prototype 原型屬性,因此輸出 undefined除了上面提到的幾點外,還要注意箭頭函式沒有一般函式其參數隱藏的 arguments 物件。
這篇的介紹就到這邊,下篇將會介紹 call, apply 和 bind 三個函式。
求輸出結果? 答案放在留言區
const shape = {
radius: 10,
diameter() {
return this.radius * 2;
},
perimeter: () => 2 * Math.PI * this.radius,
};
console.log(shape.diameter());
console.log(shape.perimeter());
MDN Arrow function expressions
Day 06: ES6篇 - Arrow Function(箭頭函式)
輸出結果是 20 和 NaN。
因為物件 shape 是宣告在全域,所以在 shape 物件內的箭頭函式 perimeter,其 this 指向也是全域,導致要取出 radius 時是在全域下查找,找不到的結果就是計算後回傳 NaN。