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