###### tags: `JavaScript 學習紀錄`
# [JS] What is “this” in JavaScript? What does “this” refer to?
## Outline
- Simple call (直接的調用)
- As an object method (物件的調用)
- Arrow Functions (箭頭函式的調用)
- As a DOM event handler (DOM 物件調用)
- Strict mode (嚴格模式調用)
- `call`, `apply`, `bind` (強制綁定 `this` 的調用)
- As a constructor (建構式調用)
## Simple call
如果直接調用函式,此函式的 this 會指向 window,以下兩個範例都是直接調用函式,所以都是指向 window
#### Example 1: this refers to window object
```javascript=
window.name = 'window';
function callName() {
console.log('call:', this.name);
}
callName(); //window
```
#### Example 2: 在function內宣並調用, simple call -> window
```javascript=
window.name = 'window';
function callName () {
console.log('call:', this.name);
// function 內的 function
function callAgainName () {
console.log('call again:', this.name);
}
callAgainName();
}
callName(); //"call:" "window" //"call again:" "window"
```
小結: ==無論在哪一層,純粹的調用方式 this 都會指向 window==
## As an object method
如果 function 是在物件下被called,那麼 this 則會指向此object,無論 function 是在哪裡宣告
#### Example 1
```javascript=
var age = 18;
var person = {
age: 28,
displayAge: displayAge
}
function displayAge() {
console.log(this.age);
console.log(this);
}
displayAge(); // 18 //window
person.displayAge();
// 28, //{age: 28, displayAge: ƒ}, 在物件下呼叫,this 則是該物件
```
#### Example 2: 把object裡面的function指給一個變數, 但還是用simple call
```javascript=
window.age = 18;
var person = {
age: 28,
displayAge: function () {
console.log(this.age);
console.log(this);
}
}
//物件內的 function指給一個變數
var callThisName = person.displayAge;
callThisName(); // 18, window
```
#### Example 3: nested object
```javascript=
window.age = 18;
function displayAge() {
console.log(this.age);
console.log(this);
}
var person = {
age: 28,
displayAge: displayAge,
nestedPerson: {
age:40,
displayAge: displayAge
}
}
displayAge(); //18 //window
person.displayAge(); //28, //{age: 28, displayAge: ƒ}. this refers to the person object
person.nestedPerson.displayAge(); //40 //{age: 40, displayAge: ƒ}. this refers to the nestedPerson object
```
## Arrow functions: they don’t have their “own” this
- 依據語彙環境的父層區域(parent scope)來綁定。白話的說,arrow function **定義位置**(不是呼叫順序)的上一層 this 代表誰,arrow function 內的 this 就代表誰
```javascript=
var person = {
age:28,
displayAge:function() {
const displayAge2 = () => {
console.log(this.age);
console.log(this);
}
displayAge2(); //arrow function->parent this
}
}
person.displayAge(); //28, {age: 28, displayAge: ƒ}
```
## As a DOM event handler
- DOM 搭配 addEventListener 時,this 指的是觸發事件的元素。以下這段程式碼可以貼在任何網頁下的 Console,接下來點擊畫面上任何一區域,該區域則會加上紅線
#### Example 1: DOM + addEventListener, this 是 DOM boject
```javascript=
const box = document.querySelector('.box')
box.addEventListener('click', function(){
console.log(this); // 這裡的 this 是 DOM box
})
```
#### Example 2: DOM + addEventListener + arrow function, this 是 parent 的 this
```javascript=
const box = document.querySelector('.box')
box.addEventListener('click', function(){
console.log(this); // DOM box
setTimeout(()=>{
console.log(this); // DOM box. arror function->parent this
},500)
})
```
## Strict mode: 讓simple call的 this 不再是全域 window, 需要給它this
- 現在會建議寫 JavaScript 的時候加入 'use strict',這可以改正一些coding不良習慣,但也有可以因此導致專案無法運作,此時可以考慮將 'use strict' 加在函式內,避免影響過去的程式碼及相關套件
#### Example 1
```javascript=
window.age = 28;
function displayAge() {
'use strict';
console.log('call:', this.age);
}
displayAge();
// Uncaught TypeError: Cannot read properties of undefined (reading 'age')
```
#### Example 2:給 strict mode 一個this, object調用 or 綁定都可以
```javascript=
window.age = 38;
const person = {
age :28,
displayAge: function displayAge() {
'use strict';
console.log(this.age);
},
}
person.displayAge(); //28. object call->this is person obj
person.displayAge.call({ age: 18 }); //18. 強制綁this
```
## 強制綁定 this
`call`, `bind`, `apply` 這三者都可以傳入新的 this 給予函式使用,使其作為 this 所指向的物件,三者僅是使用方法不同
#### `call`
- **`fn.call(this, arg1, arg2..., argn)`**
- 第一個參數:想要綁定的 this
- 第二以後的參數:想要傳進目標函式的參數,如果目標函式中不需要參數則不要傳入即可
- 功能
- 執行 function
- 明確指定 this
Example 1: 強制綁定18,永遠的18歲!
```javascript=
var age = 28;
function callName() {
console.log(this.age);
}
callName(); // 28
callName.call({age: 18}); // 18
```
Example 2: 除了傳入給定 this 的參數之外還可以傳入其他argument
```javascript=
var obj1 = {
myName: 'obj 1',
fn: function (message) {
console.log(this.myName + ' ' + message);
}
}
var obj2 = {
myName: 'obj 2',
}
obj1.fn.call(obj2, 'Hello'); //obj 2 Hello
```
#### `apply`
- **`fn.apply(this, [arg1, arg2..., argn])`**
- 第一個參數:想要綁定的 this
- 第二個參數:與`call`類似, 只是第二個參數是陣列而且必須是陣列
- 功能
- 同`call`
```javascript=
var obj1 = {
myName: 'obj 1',
fn: function (message) {
console.log(this.myName + ' ' + message);
}
}
var obj2 = {
myName: 'obj 2',
}
obj1.fn.call(obj2, ['Hello']); //obj 2 Hello
```
#### `bind`
- **`fn.bind(this, arg1, arg2..., argn)`**
- 第一個參數:想要綁定的this
- 想要傳進目標函式的參數,如果目標函式中不需要參數則不要傳入即可
- 回傳:回傳包裹後的目標函式
- 功能
- 明確指定 this
- 回傳一個包裹函式,當我們執行這個函式時,同時將arguments 一起帶進 Function 中
- 無論函數怎麼被調用,原始函數的 this 在新函數將永遠與 bind 的第一個參數綁定起來, 只能綁一次的概念
Example 1
```javascript=
function fn() {
console.log(this.a);
}
var bfn = fn.bind({a: 'Mark'});
var b2fn = bfn.bind({a: 'Rose'});
fn(); // undefind, 全域沒有定義a
bfn(); // Mark
b2fn(); // Mark. bind 只能使用一次!就算再綁Rose也沒用
```
Example 2: object call + `bind`
```javascript=
function fn() {
console.log(this.a);
}
var bfn = fn.bind({a: 'Mark'});
var b2fn = bfn.bind({a: 'Rose'});
var obj = {a: 'John', fn: fn, bfn: bfn, b2fn: b2fn};
obj.fn(), obj.bfn(), obj.b2fn();
// John, Mark, Mark. object調用在bind過後會被取代
```
## As a constructor
在建構式下會 new 一個新物件,此時的 this 會指向新的物件
```javascript=
function TeamConstructor () {
this.men = 'jamie'
}
class TeamConstructor {
constructor(man) {
this.man = man;
}
}
var myTeam = new TeamConstructor();
console.log(myTeam.men); //jamie
```
## 整理
- Simple call: window
- As an object method: object本身
- Arrow Functions: parent this
- As a DOM event handler: DOM object
- Strict mode: 給它的this
- `call`, `apply`, `bind`: 強制綁進去的`this`
- As a constructor: 建構式本身new object
## 總結
- ==Regular function: how is called ?==
- ==Arrow function: where is definded ?==
## Reference
[this: Simple call, Object method, DOM event handler, Constructor](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Operators/this)
[Regular function v.s Arrow function](https://www.youtube.com/watch?v=dWZIPIc3szg)
[Strict mode](https://www.w3schools.com/js/js_strict.asp)
[call, apply, bind](https://developer.mozilla.org/zh-TW/docs/Web/JavaScript/Reference/Global_Objects/Function/call)