DAY 21
0
Modern Web

你不可不知的 JavaScript 二三事#Day21：箭頭函數 (Arrow Functions) 的 this 和你想的不一樣 (1)

Day20 介紹箭頭函數 (Arrow Functions) 時示範了一個 `this` 的例子，可以發現和傳統函數的 `this` 運作原則大不相同。

Arrow Functions 的 `this` 判斷原則

MDN & W3Schools:

• In arrow functions, this retains the value of the enclosing lexical context's this.
• Arrow functions do not have their own this.
• In global code, it will be set to the global object.

Arrow Functions 的 `this` 和傳統函數的一個重大差異就是看的是語彙位置

1. 物件函式

1.1. 函數被定義在物件之內

傳統函數

``````var player = {
whatsThis: function() {   // normal function
return this;
},
};

console.log( player.whatsThis() === player );    // true
``````

Arrow Functions

`whatsThis()` 使用 Arrow Functions 定義，因此 `whatsThis()` 本身不會有自己的 `this`，而是沿用外圍環境的 `this`

``````var player = {
whatsThis: () => {    // arrow function
return this;
},
};

console.log( player.whatsThis() === window );    // true
``````

1.2. 借用函數 (函數被定義在物件之外)

傳統函數

``````var whatsThis = function() {   // normal function
return this;
};

var player = {};
player.f = whatsThis;

console.log(player.f() === player);     // true
``````

Arrow Functions

``````var whatsThis = () => {    // arrow function
return this;
};

var player = {};
player.f = whatsThis;

console.log(player.f() === window);     // true
``````

1.3. 物件的屬性物件的函式

傳統函數

``````var player = {
name: 'OneJar',
f: function() {
return this;
},
pet: {
name: 'Totoro',
f: function() {
return this;
},
}
};

console.log(player.f() === player);             // true
console.log(player.pet.f() === player.pet );    // true
``````

Arrow Functions

``````var player = {
name: 'OneJar',
f: () => {
return this;
},
pet: {
name: 'Totoro',
f: () => {
return this;
},
}
};

console.log(player.f() === window);         // true
console.log(player.pet.f() === window );    // true
``````

2. 簡易呼叫 (Simple Call)

2.1. 全域環境 (Global Context) 下定義函數 & 呼叫函數

傳統函數

``````var whatsThis = function() {
return this;
}

console.log( whatsThis() ); // (normal mode) window / (strict mode) undefined
``````

Arrow Functions

``````var whatsThis = () => {
return this;
}

console.log( whatsThis() ); // window
``````

2.2. 內部函數 (Inner Functions)

傳統函數

``````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();
``````

``````Output#1:  20
Output#2:  10
``````
• 「Output#1」時，呼叫方式是 `obj.f()`，因此 `this` 是呼叫者 `obj` 物件，`this.x` 是 20。
• 「Output#2」時，呼叫方式是 `foo()`，視同簡單呼叫，一般模式下 `this` 是 Global 物件，因此 `this.x` 是 10。

Arrow Functions I

``````var x = 10;
var obj = {
x: 20,
f: function(){
console.log('Output#1: ', this.x);
var foo = () => { console.log('Output#2: ', this.x); } // arrow function
foo();
}
};

obj.f();
``````

``````Output#1:  20
Output#2:  20
``````
• 「Output#1」所在的函數仍是傳統函數，因此 `this.x` 不變仍是 20。
• 「Output#2」所在的函數變成 Arrow Function，沿用外層的 `this`，其外層就是 `obj.f()`，因此 `this.x` 也是 20。

Arrow Functions II

``````var x = 10;
var obj = {
x: 20,
f: () => {  // arrow function
console.log('Output#1: ', this.x);
var foo = function() { console.log('Output#2: ', this.x); }
foo();
}
};

obj.f();
``````

``````Output#1:  10
Output#2:  10
``````
• 「Output#2」根據呼叫方式是 `foo()`，視同簡單呼叫，一般模式下 `this` 是 Global 物件，因此 `this.x` 是 10。
• 「Output#1」會沿用外層的 `this`，往外找一層是 Global Context，所以 `this` 也是 Global 物件。

Arrow Functions III

``````var x = 10;
var obj = {
x: 20,
f: () => {  // arrow function
console.log('Output#1: ', this.x);
var foo = () => { console.log('Output#2: ', this.x); } // arrow function
foo();
}
};

obj.f();
``````

``````Output#1:  10
Output#2:  10
``````
• 「Output#1」會沿用外層的 `this`，往外找一層是 Global Context，所以 `this` 也是 Global 物件。
• 「Output#2」沿用外層的 `this`，其外層是 `obj.f()`；而 `obj.f()``this` 如上面所說，經過沿用後是 Global 物件。