JavaScript Arrow Functions




箭頭函式運算式 (Arrow Function expression) 是在 ES6 新增的建立函式語法,這種寫法有著兩個重要的特性:

  • 更簡短的函式寫法

  • this 變數強制綁定

箭頭函式(Arrow Function)

傳統的函式寫法:

1
2
3
4
5
6
7
8
9
10
11
//沒參數
let myFunc = function(){
return 1;
}
//有參數
let fn = function(n1, n2){
return n1+ n2;
}

myFunc(); // 1
fn(1,2); // 3

ES6 的箭頭函式寫法:

1
2
3
4
5
6
7
//沒參數
let myFunc = () => (1);
//有參數
let fn = (n1, n2) => (n1+n2);

myFunc(); // 1
fn(1,2); // 3

在使用箭頭函式中,如果函式沒有帶上參數記得加空括號 () ,有參數直接在 () 內填入參數即可。

如果函式最後要回傳值的話,可以省略 return 不寫。

當函式只有一個參數時,不需要使用括號

從上面的例子可以發現,當函式無參數或有兩個以上的參數時,都要加上括號 ( );當函數只有一個參數時,可以省略括號不寫。

1
2
3
4
5
6
//兩種寫法都可以
//有加括號
let fn = (n1) => (n1);
//沒加括號
let fn = n1 => n1;
fn(1); // 1

我們使用括號 () 來定義函式帶入的參數,而大括號 {} 定義函式功能的一些 JavaScript 語句,如果函式有多行語句(表達式)時就要使用 {} , {} 內需自行加入 return ,否則會出現 undefined

1
2
3
4
5
let fn = (n1) => { n1 }
let myfunc = (n1) => { return n1; }

fn(1); // undefined
myfunc(1); // 1

匿名函式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//第一個範例
//傳統的匿名函式
const sum = function(a, b) {
return a+b;
}

//匿名的箭頭函式
const sum_arr = (x, y) => {
return x + y;
};

//第二個範例
//傳統的匿名函式
let fn = function(){
setTimeout(function(){
console.log("1秒");
},100);
}

//匿名的箭頭函式
let fn_arr = function(){
setTimeout(() => {
console.log("1秒");
},100);
}

sum(1,2); // 3
sum_arr(1,2); // 3
fn(); // 1秒
fn_arr(); // 1秒

指定參數的預設值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//傳統寫法
function fn(name){
if(name == undefined){
name= "Cyting";
}
console.log(name);
}

//ES6寫法
function fn(name ="Cyting"){
console.log(name);
}

//箭頭函式寫法
let fn = (name="Cyting") => {
console.log(name);
}

fn("Cheng-Yi-Ting!"); // Cheng-Yi-Ting!
fn(); // Cyting

箭頭函式的 this

不管是使用傳統的函式寫法或箭頭函式, this 都會存取到 window 物件(不清楚原因的話請先參考之前的文章 JavaScript THIS )。

1
2
3
4
5
6
7
8
9
10
let fn = function(){
console.log(this);
}

let fn2 = () => {
console.log(this);
}

fn(); //[Window Object]
fn2(); //[Window Object]

funcsetTimeout 箭頭函式的 this 均會指向 obj

1
2
3
4
5
6
7
8
9
10
11
const obj = { a:1 }

function func() {
console.log(this.a) // 1
setTimeout(() => {
console.log(this.a) // 1
}, 1000)

}

func.call(obj)

如果是傳統的函式寫法, setTimeoutthis 會指向 window 物件:

1
2
3
4
5
6
7
8
9
10
const obj = { a: 1 }

function func() {
console.log(this.a) // 1
setTimeout( function() {
console.log(this.a) // undefined
}, 1000)
}

func.call(obj)

我們可以透過將 this 存在 that 變數中,之後就可以透過 that 存到之前 this 的參照:

1
2
3
4
5
6
7
8
9
10
const obj = { a: 1 }

function func(){
const that = this
setTimeout(function(){
console.log(that.a) // 1
}, 1000)
}

func.call(obj)

也可以透過 .bind() 來強制指定 this 為 () 內的物件,於是可以把 setTimeout 裡面的 this 指定為先前的 this

1
2
3
4
5
6
7
8
9
const obj = { a: 1 }

function func(){
setTimeout(function(){
console.log(this.a) // 1
}.bind(this), 1000)
}

func.call(obj)

傳統函式的 this 是根據呼叫方式的不同而有所不同;箭頭函式的 this 是綁定到其定義時所在的物件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
var name = 'Cheng-Yi-Ting'
var obj = {
name: 'Cyting',
fun1: function () {
// 注意,這裡是 function,以此為基準產生一個作用域
console.log('1', this.name); // 1 'Cyting',
setTimeout(() => {
console.log('2', this.name); // 2 'Cyting',
console.log('3', this); // 3 obj 這個物件
}, 1000);
},
fun2: () => {
// 注意,如果使用箭頭函式,this 依然指向 window
console.log('4', this.name); // 4 'Cheng-Yi-Ting'
setTimeout(() => {
console.log('5', this.name); // 5 'Cheng-Yi-Ting'
console.log('6', this); // 6 window 物件
}, 1000);
}
}

obj.fun1();
obj.fun2();

一般函式是建立在 window 底下,所以箭頭函式自然會指向 window ;可以透過將箭頭函式宣告在物件內部,來將 this 指向該物件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
var func = function () {
var func2 = function () {
setTimeout(() => {
console.log(this);
}, 1000);
};

var func3 = {
func: func2,
value: 1
}
func2(); // this = window
func3.func(); // func3 Object
}
func();

func3() 是呼叫在物件內的函式,因此箭頭函式會是使用它所在的物件。

不可使用箭頭函式的情況

apply, call, bind

函式物件中的 callapplybind 這三個方法,無法覆蓋箭頭函式中的 this 值:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
let obj = {
value: 1
}

const a = () => {
console.log(this);
}

const b = function () {
console.log(this);
}

a.call(obj); // 箭頭函式的情況,this 依然是 window
b.call(obj); // 一般函式 this 則是傳入的物件

建構函式

箭頭函式無法像一般的函式一樣作為建構式使用,使用 new 語法會出現錯誤:

1
2
3
4
5
6
const Message = (text) => {
this.text = text
}

const helloMessage = new Message('Hello World!');
// "TypeError: Message is not a constructor"

DOM事件處理函式

在 HTML 中建立一個 button element,在 button 使用 addEventListener 事件監聽器:

1
2
3
4
5
6
7
8
9
var button = document.querySelector('button');
var fn_arr = () => {
console.log(this) // this 指 Window
};

var fn = function(){
console.log(this) // this 指 HTMLButtonElement
}
button.addEventListener('click', fn_arr);

使用傳統的寫法,在觸發這個事件時所指稱的對象會從原本的 window 變成按鈕物件;若使用的是箭頭函式,則 this 一樣會是 window 物件。

Prototype 中定義的方法

如果在原型中使用箭頭函式,此時箭頭函式內的 this 會指向 window ,若是在嚴格模式則會是 undefined

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Person(firstName, lastName, age) {
this.firstName = firstName;
this.lastName = lastName;
this.age = age;
}

Person.prototype.log1 = function () {

console.log(this.firstName+' '+this.lastName+ ', age:' + this.age);
}
Person.prototype.log2 = () => {

console.log(this.firstName+' '+this.lastName+ ', age:' + this.age);
}

const roman = new Person('Roman', 'Gonzalez', 18);

roman.log1(); // Roman Gonzalez, age:18

roman.log2(); // undefined undefined, age:undefined

結論

箭頭函式語法

  1. 沒有參數時要有小括號。
  2. 只有一個參數時可以省略小括號。
  3. 若有兩個以上的參數要有小括號。
  4. 只有一行回傳值可以省略大括號。

箭頭函式限制

  • 函式物件中的 callapplybind 這三個方法,無法覆蓋箭頭函式中的 this 值。
  • 不可作為建構式使用,會在使用 new 時候拋出錯誤。
  • 箭頭函式並沒有原型(prototype)屬性。
  • 沒有一般函式有的隱藏 arguments 物件。
  • 箭頭函式不能當作 generators 使用,使用 yield 會產生錯誤。

參考文獻

  1. https://dotblogs.com.tw/shihgogo/2017/12/11/111122

  2. https://ithelp.ithome.com.tw/articles/10185221

  3. https://ithelp.ithome.com.tw/articles/10227798

  4. https://pjchender.blogspot.com/2017/01/es6-arrow-function.html?m=1

  5. https://wcc723.github.io/javascript/2017/12/21/javascript-es6-arrow-function/

  6. https://eyesofkids.gitbooks.io/javascript-start-from-es6/content/part4/arrow_function.html