this 是 JavaScript 的一個關鍵字, this 會在不同的情境下指稱到不同的物件。
this 在物件導向裡面,它所代表的就是那個 instance 本身:
| 1 | class Car { | 
在上面我們宣告了一個 class Car,寫了 setName 跟 getName 兩個方法,在裡面用this.name來取放這個 instance 的屬性,myCar.setName('Ford'),所以 this 就會是myCar。
this 不等於 function
如果直接調用函式,此函式的 this 會指向 window 。
| 1 | window.say = 'Hi'; | 
this  不會指到 CallSay  這個 function,實際上是指向  window  。
再看一個範例:
| 1 | var call = function() { | 
在這個範例中, say 可以透過 this.call 取得 call ,是因為 this.call 實際上是指向 window.call。
而 call 的 this.name 並非是 say 中的 Felix ,而是指向 window.name ,所以會得到 undefined 的結果。
下個例子是將 function 內在包覆著 function,但只要是直接呼叫,this 都是屬於全域。
| 1 | window.say = 'Hi'; | 
來看一下巢狀迴圈中的 this :
| 1 | var obj = { | 
在  obj.func1()  裡面的  this  會指向  func1 ;是因為  func1  透過  obj  來呼叫的。
但  obj.func1()  裡面的 func2() 在執行時的  this  卻會指向 window。
也就是當沒有特定指明 this 的情況下,預設綁定 (Default Binding) this 為 「全域物件」,也就是 window。
以上這幾種情況, this 的值在瀏覽器底下就會是 window  ,在 node.js 底下會是  global  ,如果是在嚴格模式,this 的值就會是 undefined  。
強制指定 this 的方式
在 JavaScript 有三個可以強制指定 this 的方式,分別是 call() 、 apply() 以及 bind()。
call  跟 apply  是很類似的方法,這兩種都是能夠呼叫 fucntion 的函式
| 1 | ; | 
因為是嚴格模式所以  funA(1, 2)  的 this 是 undefined。
call 和 apply 就是你第一個參數傳什麼,裡面 this 的值就會是什麼。儘管原本已經有 this,也依然會被覆蓋:
而兩者的差別只在於 apply 傳進去的參數是一個 array,所以上面這三種呼叫 function 的方式是等價的。除了直接呼叫 function 以外,你也可以用 call 或是 apply 去呼叫,差別在於傳參數的方式不同。
除了以上兩種以外,還有最後一種可以改變 this 的方法:bind。
| 1 | ; | 
在這邊我們把  funA  這個 function 用  Hi  來綁定,所以最後呼叫  myFunA  時會輸出  Hi  。
在看一個例子:
| 1 | var obj = { | 
加上了  bind  之後的  func.bind(obj)()  ,會替我們將  func  的  this  暫時指向我們所設定的 obj。
於是  console.log(this.LastName)  的結果自然就是  obj.LastName  也就是  Lincoln  了。
重新指向 this
假設我們今天在某個元素上透過  addEventListener  註冊了  click  事件,而在事件中的  this  指的是「觸發事件的元素」。
要是我們在事件的 callback function 加入 ajax 的請求,那麼根據前面所說的,預設綁定 (Default Binding) 會把這個 callback function 的  this  指定給  global object  ,也就是  window  。
如果需調用的則是物件本身的話,可以先用一個變數指向 this,等到調用後再重新使用它。
| 1 | el.addEventListener("click", function(event) { | 
像這樣,我們將事件內的  this  先用一個叫  that  的變數儲存它的參考,那麼在 ajax 的 callback function 就可以透過  that  來存取到原本事件中的  this   了。
如果我們把 call 跟 bind 同時用會怎樣!?
| 1 | ; | 
答案是不會改變,一但 bind 了以後值就不會改變了。
在非嚴格模式底下,無論是用 call、apply 還是 bind,你傳進去的如果是基本型別都會被轉成 object 。
舉例來說:
| 1 | function funA() { | 
物件中的 this
最前面我們示範了在物件導向 class 裡面的 this,但在 JavaScript 裡面還有另外一種方式也是物件:
| 1 | const obj = { | 
這種跟一開始的物件導向範例不太一樣,這個範例是直接創造了一個物件而沒有透過 class,所以你也不會看到 new 這個關鍵字的存在。
舉個簡單的例子來幫大家複習一下 Scope Chain:
| 1 | var value = 1 | 
無論怎麼呼叫 say  這個 function,印出來的 value 永遠都會是全域變數的   value  ,因為 say  在自己的作用域底下找不到  value  於是往上一層找,就會找到  global scope ,這跟你在哪裡呼叫 say  一點關係都沒有。 say  這個 function 在「定義」的時候就把 scope 給決定好了。
但 this 卻是完全相反,this 的值會根據你怎麼呼叫它而變得不一樣,像是先前我們剛講過的 call、apply 跟 bind ,你可以用不同的方式去呼叫 function,讓 this 的值變得不同。
this 的值跟作用域跟程式碼的位置在哪裡完全無關,只跟「如何呼叫」有關。
讓我們看複雜一點的例子:
| 1 | const obj = { | 
say  因為沒有傳參數進去,所以是預設綁定,在非嚴格模式底下是  window  ,所以會  window.value  也就是 undefined  。
可以透過把 function 的呼叫轉成用 call 的形式,就會較容易看出 this 的值是什麼。
| 1 | obj.func2.say() // obj.func2.say.call(obj.func2) => 2 | 
箭頭函式的 this
從 ES6 開始新增了一種叫做 「箭頭函式表示式」 (Arrow Function expression) 的函式表達式。
而箭頭函式有兩個重要的特性:
- 更簡短的函式寫法
- this 變數強制綁定
ES6 新增的箭頭函式中的 this 有不一樣的運作方式,只要記住「在宣告它的地方的 this 是什麼,它的 this 就是什麼」,什麼意思呢?讓我們看個範例:
| 1 | const obj = { | 
我們在 say  這個 function 裡面宣告了  func1  這個箭頭函式,所以 say  的 this 是什麼, func1  的 this 就會是什麼。
箭頭函式的 this 不是取決於在宣告時那個地方的 this。