JavaScript ES6 - 箭頭函式與傳統函式的差異

箭頭函式其實有一些陷阱,大部分情況下使用傳統函式其實就夠用了。

將傳統函式改寫成 “箭頭函式”

下方範例是我們一般傳統函式的寫法:

1
2
3
4
var callSomeone = function (someone) {
return someone + '吃飯了';
};
console.log(callSomeone('小明'));

如果要將傳統函式改寫成箭頭函式 (Arrow Function),應該怎麼做呢?

答案是把 function 字眼拿掉並加上箭頭 =>

1
2
3
4
var callSomeone = (someone) => {
return someone + '吃飯了';
};
console.log(callSomeone('小明'));

上面這個就是我們最常使用的箭頭函式的形式了!

當然,箭頭函式也能做一些縮寫。

某些情況下還能再 “縮寫”

當箭頭函式的內容只有 “一行” 的情況下,可以省略大括號 {},並把 return 拿掉。

單行情況下會自動 return

這樣就變成只有一行的形式:

1
2
var callSomeone = (someone) => someone + '吃飯了';
console.log(callSomeone('小明'));

而且參數 (someone) 的括號 () 也可以省略:

1
2
var callSomeone = (someone) => someone + '吃飯了';
console.log(callSomeone('小明'));

但是,假如預設是 “不帶入” 參數的,那這個括號是不能省略的,至少要留一個括號:

1
2
var callSomeone = () => '小明' + '吃飯了';
console.log(callSomeone());

箭頭函式沒有 Arguments 參數

所謂 Arguments 參數,就是當我們帶入一堆數值,但是我們並沒有帶入函式的參數名稱時,可以用 arguments 這個變數來取代它們。

例如:

1
2
3
4
5
const updateEasyCard = function () {
let cash = 0;
console.log(arguments);
};
updateEasyCard(10, 50, 100, 50, 5, 1, 1, 1, 500);

但如果我們今天把這個函式改寫為箭頭函式,會出現 arguments is not defined 的報錯訊息。

原因是因為箭頭函式並沒有 Arguments 參數。

解法:在箭頭函式裡使用 “其餘參數”

如果想要在箭頭函式中,達成類似 Arguments 參數那樣 “大量使用參數” 的效果,可以使用之前學過的 “其餘運算符”。

使用 “其餘參數”:

1
2
3
4
5
const updateEasyCard = (...arg) => {
let cash = 0;
console.log(arg);
};
updateEasyCard(10, 50, 100, 50, 5, 1, 1, 1, 500);

This 綁定的差異

this 的綁定是箭頭函式與傳統函式相差最大的地方,這裡也有許多陷阱。

傳統函式的 This

我們先看以下範例:

1
2
3
4
5
6
7
8
9
10
11
12
var name = '全域阿婆';
var auntie = {
name: '漂亮阿姨',
callName: function () {
console.log('1', this.name, this); // 1 漂亮阿姨
setTimeout(function () {
console.log('2', this.name); // 2 漂亮阿姨
console.log('3', this); // 3 auntie 這個物件
}, 10);
},
};
auntie.callName();

我們明明是在 CallName 裡面執行 setTimeout,為什麼第二個 this 卻是指向全域呢?

Imgur

這是因為傳統函式的 this 是指向 **”函式的呼叫方式”**。

而這個 setTimeout 其實就等於 window.setTimeout
所以這裡的 this 是指向 window,也就是全域。

箭頭函式的 This

我們把剛才的範例改寫成箭頭函式。
完成後,會發現所有的 this 都是指向全域。

1
2
3
4
5
6
7
8
9
10
11
12
var name = '全域阿婆';
var auntie = {
name: '漂亮阿姨',
callName: () => {
console.log('(Arrow) 1', this.name, this); // 1 漂亮阿姨
setTimeout(() => {
console.log('(Arrow) 2', this.name); // 2 漂亮阿姨
console.log('(Arrow) 3', this); // 3 auntie 這個物件
}, 10);
},
};
auntie.callName();

這是因為在物件內使用箭頭函式時,this 所指向的 “可能” 是全域。

為什麼是 “可能” 呢?
因為箭頭函式的 this 不一定是綁定在 window 上,可能是在其他物件上。

箭頭函式的 this 是綁定在它定義時所在的物件上。

觀念整理

在 Vue 的 methods 裡,建議用傳統函式

根據上面的案例,我們知道使用箭頭函式時,會比較不好掌握 this 的使用。

像是我們在寫 Vue 的 methods 時,很常使用 this 去取用元件的 data,例如 this.name 這類的寫法。
這種時候如果我們使用的是 “箭頭函式”,那就滿可能會出錯的!

所以在 Vue 的 methods 裡面,會比較建議使用 “傳統函式” 配合 “縮寫” 的方式來呈現,例如:

1
2
3
4
5
6
7
8
// 傳統函式 + 縮寫:callName(){...}
callName () {
console.log('1', this.name, this);
setTimeout(function () {
console.log('2', this.name);
console.log('3', this);
}, 10);
},

這個寫法就跟傳統函式是一樣的結果。

那麼,到底什麼時候要用箭頭函式呢

這邊就說說該如何善用的箭頭函式。

下方範例中,內層的 console.log 是指向全域,
我們該如何將這個 this 改為指向 auntie 這個物件呢?

1
2
3
4
5
6
7
8
9
var auntie = {
name: '漂亮阿姨',
callName() {
setTimeout(function () {
console.log(this);
}, 10);
},
};
auntie.callName();

這時候箭頭函式就能出場了。

一樣外層的 callName 是個傳統函式,但是內層的 setTimeout 我們把它改為箭頭函式。

接著宣告 var vm = this;,這時候的 vm 就是指向 auntie 這個物件。

1
2
3
4
5
6
7
8
9
10
11
var auntie = {
name: '漂亮阿姨',
callName() {
var vm = this;
// 改寫成箭頭函式
setTimeout(() => {
console.log(this, vm, vm.name);
}, 10);
},
};
auntie.callName();

有點像是我們之前取用 Vue 的 data 的做法。

Imgur

補充:箭頭函式沒有自己的 This

「箭頭函式沒有自己的 this」,要判斷 this 基本上都是去找外層的 this

最後再補充一下,上面只要 setTimeout 改成箭頭函式,this 就直接指向 auntie 了。

因為 setTimeout 為箭頭函式,所以要尋找它的 this 必須找它外層的 this
而它的外層 callName 如果是以 anuntie.callName 來呼叫,那便是指向 anuntie
所以 this.name 就等於 漂亮阿姨

以上資源是我自己整理過後的筆記,若有錯誤歡迎隨時和我聯繫。