# JavaScript - this
###### tags: `JavaScript` `讀書會` `this`
## `Call()` & `Apply()` 運用
### Function 調用方式
1. `fn(arguments)` ( 一般調用,屬於語法糖 )
2. `fn.call(this指向, arguments)` ( 隱式調用 )
3. `fn.apply(this指向, [arguments])` ( 隱式調用 )
```jsx=
function test() {
console.log('test')
}
test() === test.call() // true
test() === test.apply() // true
```
### `call` 和 `apply` 的區別僅僅是第二個參數的差異
1. `call` 可以從第 2 個參數傳到 n 個參數 `fn.call(undefined, 1, 2, 3, 4...)`。
2. `apply` 在第 2 個參數傳入陣列,後面再傳入則無效 `fn.apply(undefined, [1, 2, 3, 4...])`。
<br>
`call()` 可以傳入多個參數 :
```jsx=
function test(a, b, c){
console.log(arguments)
}
test.call(undefined, 1, 2, 3)
// Arguments(3) [1, 2, 3]
```
`apply()` 則是傳入一個陣列參數 :
```jsx=
function test(a, b, c){
console.log(arguments)
}
test.apply(undefined, [1, 2, 3])
// Arguments(3) [1, 2, 3]
```
`call()` 傳入陣列
```jsx=
function test(a, b, c){
console.log(arguments)
}
test.call(undefined, [1, 2, 3])
// Arguments [[1, 2, 3]]
```
`apply()` 傳入多個陣列參數沒用
```jsx=
function test(a, b, c){
console.log(arguments)
}
test.apply(undefined, [1, 2, 3], [4, 5, 6], [7, 8, 9])
// Arguments(3) [1, 2, 3]
```
### `call` 和 `apply` 是為了改變 `this` 指向而存在的
```jsx
const obj1 = {
num: 20,
fn(n) {
console.log(this.num + n)
}
}
const obj2 = {
num: 15,
fn(n) {
console.log(this.num - n)
}
}
obj1.fn(10) // ?
obj1.fn.call(obj2, 10) // ?
```
:::spoiler Answer1
30,由 `obj1` 呼叫 `fn(10)`,因此 `this` 指向 `obj1`
:::
<br>
:::spoiler Answer2
25,因為 `this` 已經指向 `obj2`,所以 `obj1` 的 `fn(n)` 裡面所取得的 `this.num` 會是 `obj2` 的 `num: 15`
:::
## 如何得知 this 指向誰 ?
<aside>
💡 一旦脫離了物件,不太需要關注 this 的值,因為沒什麼意義。
</aside>
<br>
```jsx
// 無意義的 this
function hello() {
console.log(this)
}
hello()
```
1. 嚴格模式回傳 `undefined`
2. 非嚴格模式回傳 `window`
3. 非嚴格模式,node.js 回傳 `global`
<aside>
💡 this 的值跟作用域跟程式碼的位置在哪裡完全無關,只跟「你如何呼叫」有關。
</aside>
<br>
範例 :
```jsx
const obj = {
num: 1,
hello() {
console.log(this)
}
}
obj.hello() // {num: 1, hello: ƒ}
const hey = obj.hello
hey() // window
```
誰呼叫函式,就把誰放進 call 第一個參數 :
```jsx
obj.hello.call(obj) // this 指向 obj,印出 {num: 1, hello: ƒ}
hey.call() // this 指向 window,印出 Window{...}
```
變異題 :
```jsx
const num = 10
const obj = {
num: 20,
fn() {
function test(){
console.log(this.num)
}
test()
}
}
obj.fn() // ?
```
:::spoiler Anwser
答案是 `undefined`
真正呼叫 `this` 的是 `test` 這個函式,改成使用 call 呼叫 : `test.call()`,在沒有指定 call 第一個參數時,都是預設綁定全域,也就是 `window`。
若今天外層的 `num` 是使用 `var` 宣告,則答案會是 `10`,但 `let` 和 `const` 都有自己的區塊作用域,因此在 `this` 指向全域下讀取不到 `num`,所以為 `undefined`。
:::
## this 面試題
### 1. this 是什麼 ? 什麼是 this ? this 是什麼是 this ? 什麼是 this 是什麼 ? ( 夠了...
:::spoiler Answer
每一個 function 在被執行的時候都會有一個 reference ( 參照 ) 指向 **所屬的環境** ,這就是 this。
:::
### 2. Function 有哪些調用的方式 ? Function 裡面的 this 又是指向誰 ?
:::spoiler Answer
<br>
1. **一般調用**
`fn()`,function ( `fn()` ) 的 `this` 指向全域物件,非嚴格模式下為 `window`,嚴格模式下為 `undefined`。
2. **物件中調用**
`obj.fn()`,function ( `obj.fn()` ) 裡面的 this 指向呼叫他的物件 ( `obj` )。
3. **建構式中調用**
`const john = new Person()`,建構式 ( `Person` ) 的 `this` 指向被建構的物件 ( `john` )。
4. **隱式調用**
`fn.call()` `fn.apply()` `fn.bind()`,使用 call、apply、bind 方式將第一個參數指定 this 指向任何物件。
:::
### 3. 以下這個 this 代表什麼呢 ?
```jsx
function a() {
console.log(this)
}
```
<br>
:::spoiler Answer
不知道,this 的值與作用域和程式碼位置完全無關,只跟「你如何呼叫」有關。
:::
<br>
## Reference
* [淺談 JavaScript 頭號難題 this:絕對不完整,但保證好懂](https://blog.huli.tw/2019/02/23/javascript-what-is-this/)
* [[面試] 前端工程師一定要會的 JS 觀念題-中英對照之上篇](https://medium.com/starbugs/%E9%9D%A2%E8%A9%A6-%E5%89%8D%E7%AB%AF%E5%B7%A5%E7%A8%8B%E5%B8%AB%E4%B8%80%E5%AE%9A%E8%A6%81%E6%9C%83%E7%9A%84-js-%E8%A7%80%E5%BF%B5%E9%A1%8C-%E4%B8%AD%E8%8B%B1%E5%B0%8D%E7%85%A7%E4%B9%8B%E4%B8%8A%E7%AF%87-3b0a3feda14f#ff68)