探討 ES6 的招牌菜 Arrow Function

Arrow function 是 ES6 最重要的發明,讓 FRP 能以更簡潔的方式呈現,TypeScript 當然可使用,在 Angular 也隨處可見,如 RxJS 就必須大量使用 arrow function,是學習 Angular 一定要會的。

Version


TypeScript 2.3

以 => 取代 Anonymous Function


Arrow function 可以用來取代 anonymous function。

1
2
3
4
5
6
7
let hello = function (firstName: string, lastName: string): string {
return `Hello ${firstName} ${lastName}`;
};

message = hello('Sam', 'Xiao');

console.log(message); // Hello Sam Xiao

後面是 anonymous function,但我們可發現,function 事實上是個虛字。

1
2
3
4
5
6
7
let hello = (firstName: string, lastName: string): string => {
return `Hello ${firstName} ${lastName}`;
};

message = hello('Sam', 'Xiao');

console.log(message); // Hello Sam Xiao

function 省略,改以 => 取代。

但又發現 return 也是虛字。

1
2
3
4
5
let hello = (firstName: string, lastName: string): string => `Hello ${firstName} ${lastName}`;

message = hello('Sam', 'Xiao');

console.log(message); // Hello Sam Xiao

因為只有單一 return,可將 {return} 一起省略。

1
2
3
4
5
let hello = (firstName: string): string => `Hello ${firstName}`;

message = hello('Sam');

console.log(message); // Hello Sam

假如我們只保留 firstName 1 個參數。

1
2
3
4
5
let hello = firstName => `Hello ${firstName}`;

message = hello('Sam');

console.log(message); // Hello Sam

因為只有單一參數,連 () 都可省略。

以 => 取代 Callback


Arrow function 可以用來取代 callback。

1
2
3
4
5
6
7
8
9
10
11
const angular = [
'Kevin',
'Jeff',
'Jimmy'
];

const length = angular.map(function(person) {
return person.length;
});

console.log(length); // [5, 4, 5]

如常用的 map(),會需要我們傳入 callback,決定要回傳的新陣列。

但我們可發現,function 事實上是個虛字。

1
2
3
4
5
6
7
8
9
10
11
const angular = [
'Kevin',
'Jeff',
'Jimmy'
];

const length = angular.map((person) => {
return person.length;
});

console.log(length); // [5, 4, 5]

可將 function 省略,改用 =>

但又發現 return 也是虛字。

1
2
3
4
5
6
7
8
9
const angular = [
'Kevin',
'Jeff',
'Jimmy'
];

const length = angular.map((person) => person.length);

console.log(length); // [5, 4, 5]

因為只有單一 return,可將 {return} 一起省略。

1
2
3
4
5
6
7
8
9
const angular = [
'Kevin',
'Jeff',
'Jimmy'
];

const length = angular.map(person => person.length);

console.log(length);

因為只有單一參數,連 () 都可省略。

語法規則


多參數

1
(param1, param2, …, paramN) => { statements }
  • function=> 取代。
  • 若有多行程式,須以 {} 包起來。
1
2
(param1, param2, …, paramN) => expression
(param1, param2, …, paramN) => { return expression;}
  • function=> 取代。
  • 若只有單一 return,可將 {return } 拿掉。

單一參數

1
2
3
(singleParam) => { statements }
singleParam => { statements }
singleParam => expression
  • function=> 取代。
  • 若有多行程式,須以 {} 包起來。
  • 若只有單一 return,可將 {return } 拿掉。
  • 單一參數,可連 () 都拿掉。

無參數

1
2
() => { statements }
() => expression
  • function=> 取代。
  • 若有多行程式,須以 {} 包起來。
  • 若只有單一 return,可將 {return } 拿掉。
  • 無參數必須保留 ()

回傳物件

1
params => ({foo:bar})
  • function=> 取代。
  • 多參數/單一參數/無參數的規則依舊。
  • 將回傳物件外面加上 ()

因為 {} 已經被物件使用,只好改用 ()

Anonymous Function 與 this


在 ES5 時,anonymous function 搭配 this 時,總讓人很糾結。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var Foo ={
name: "Sam",

handleMessage: function (message, callback){
callback(message);
},

receive: function () {
this.handleMessage("Hello, ", function () {
console.log(this.name);
});
}
}

Foo.receive(); // undefined

結果執行錯誤,this.name 為 undefined。

因為 anonymous function 的 this,並不是指向 Foo,所以存取不到 this.name

在 ES5,我們會這用以下寫法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var Foo ={
name: "Sam",

handleMessage: function (message, callback){
callback(message);
},

receive: function () {
that = this;
this.handleMessage("Hello, ", function () {
console.log(that.name);
});
}
}

Foo.receive(); // Hello Sam

第 9 行

1
that = this;

receive scope 的 that 指向 this

11 行

1
console.log(that.name);

改用 that.name,就可以正確顯示 Hello Sam

很多人搞不懂 this 是什麼,就乾脆都寫成 that = this。

Arrow Function 與 this


我們剛剛知道,在ES6 可以使用 arrow function 取代 anonymous function。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var Foo ={
name: "Sam",

handleMessage: function (message, callback){
callback(message);
},

receive: function () {
this.handleMessage("Hello, ", () => {
console.log(this.name);
});
}
}

Foo.receive(); // Hello Sam

使用 arrow function 後,this 就能如預期的抓到 this.name,顯示 Hello Sam

透過 TypeScript Playground,我們來看看到底有什麼黑魔法:

1
2
3
4
5
6
7
8
9
10
11
12
13
var Foo = {
name: "Sam",
handleMessage: function (message, callback) {
callback(message);
},
receive: function () {
var _this = this;
this.handleMessage("Hello, ", function () {
console.log(_this.name);
});
}
};
Foo.receive(); // Hello Sam

第 7 行

1
var _this = this;

原來編譯成 ES5 之後,TypeScript 自動幫我們加上 _this = this,因此我們可以抓到 name

雖然 anonymous function 與 arrow function 對 this 的觀點不同,但並沒有誰對誰錯,只是應用場合不同,當你比較需要類似 OOP 的方式,就使用 arrow function;若比較需要 FRP 的方式,就使用 anonymous function,當你手中不再只有錘子,所看的東西就不再只是釘子。

Conclusion


  • Arrow function 是 FRP 的重要推手,讓我們可以使用更精簡的方式使用 callback,將更多的虛字拿掉,只留下商業邏輯中最關鍵的部分。
  • this 一直是 JavaScript 頗具爭議之處,arrow function 讓我們有另外一種方式使用 this,我們可以視需求決定該使用 arrow function 或 anonymous function。

Reference


TypeScript, Handbook : Functions
Egghead.io, Arrow Function => in ES6

2017-06-17