# 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)