JavaScript this
this 是什麼?
- this 用來 ‘傳遞’ 一個 object 的引用
 - function 呼叫方法會影響 this
 - 在同個範圍(global,function)裡面傳遞不同的東西(物件,變數)
 
this 判定方式
- 判斷函數被調用時 this 指向誰,立馬看 ( ) 左邊的部分
- 如果 ( ) 左邊是一個引用(參照),那麼函數 this 指向的就是這個引用所屬的對象
 - 否則 this 指向的就是全局對象(global)
 - this 會因執行的環境與上下文 (context) 的不同,而有不同的結果
 
 
1  | var foo = {  | 
context 是什麼?
- 函式在被呼叫執行時,所處的物件環境
 
‘use strict’
- 在非嚴格模式下,this 的內容就會是global 物件
 - 在嚴格模式下,this 的內容就會是 undefined
 
有什麼方法
- 默認綁定 Global Object (Window 物件)
 - 隱式綁定 函數功能(function)
 - 顯式綁定 (call、apply、bind)
 - 建構式綁定(constructor)
 - DOM
 
方法有什麼效果
默認綁定 Global Object (Window 物件)
- 不加任何的修飾符直接調用函數
 - 瀏覽器的執行環境下,global object 指的就是 Window 物件
 - NodeJS 的執行環境下,global object 指的就是 Global 物件
 
1  | console.log(this) // Window  | 
隱式綁定 函數功能(function)
- 呼叫的物件不同,所以執行的結果也會不同
 
1  | // 回到上一層 this = widows 在指定到 a,b  | 
- 物件在指向的時候是 by reference 的方式
 - window 錯誤 - 在物件方法內再建立方法(function)時,物件屬性值指向 window 並經由 scop china 找到上一層的物件屬性值 所以 再次設定的物件屬性不會等於正確設定的屬性值
 可由 let self = this , let that = this 來設定取代
一般寫法
1  | // f1 function 執行完後離開 execution stack ,所以在執行 f2 function 會指定到 window  | 
- 嚴格模式 ‘use strict’
 
1  | // 嚴格模式 不允許使用內層 this  | 
- 解法
 
1  | // 解法1.指定變數給 this  | 
顯式綁定 (call、apply、bind)
- 為什麼要用 顯示綁定?
- callback function 中的 this 往往會改變指向,造成不是我們要的結果
 - 明確(強制)指定要執行 function 中的 this 是什麼,就不用透過另一個變數來暫存 this 的方式來獲取
 
 
function method
- call
function.call (thisArg, arg1, arg2, ...)- 提供的 this 值與傳入參數值(arguments)傳進目標函式
 
 
- apply
function.apply (thisArg, [argsArray])- this 值傳入外,另一個傳入參數值使用 array
 
 
- call apply 兩者共通
- 重新定義函數的執行環境(this)
 - 使用在 context 較常變動的場景,依照呼叫時的需要帶入不同物件作為該 function 的 this
 - thisArg 可以指定為 null,this 指向為 window
 - 呼叫的當下就立即執行
 
 
- call apply 差異
- 傳入參數的形式不同
 
 
1  | 貓吃魚,狗吃肉,有一天貓不僅想吃肉,還想吃豬肉牛肉羊肉  | 
- 以 DOM 來看 call 和 apply 可以用來重新定義函數的執行環境(this的指向)
 
1  | function changeStyle(attr, value){  | 
- thisArg 可以指定為 null,this 指向為 window
 
1  | function add(a,b){  | 
- Math 中用 apply 把 array 當參數傳入直接求 array 中最大值,不用自己去遍歷求值
 
1  | let arr = (6,3,2,8,9)  | 
- bind
function.bind (thisArg[, arg1[, arg2[, ...]]])- 前者為套用 this 的物件,後者以後都是 function 的參數
 
- 使用 bind 寫死要綁定的物件,可避免函式呼叫時退回到預設綁定
 - 執行 function 前,綁定指定的物件這樣 this 就會是這個物件
 - 新函式在呼叫時,建立一個新函式提供的 this 值與一連串的傳入參數值來進行呼叫,不需要參數則不要傳入即可
 - 常用在像是 callback function 這種類型,可以先綁定好 this,然後讓 function 在需要時才被呼叫
 - 不會立即執行
 
 
1  | var obj = {  | 
- 硬綁定(hard binding)是指使用 bind 寫死要綁定的物件,可避免函式呼叫時退回到預設綁定
 
1  | /*  | 
function currying
- 類似預設參數
 - 將接受 n 個參數的 function,轉變成 n 個到只接受一個參數的 function 過程
 - 簡化參數的處理,基本上是一次處理一個參數,藉以提高程式的彈性和可讀性
 
1  | // setTimeout function 的 this 指向為 window  | 
- 實例
 
1  | // function currying  | 
建構式綁定(constructor 物件產生時會被呼叫的方法)
- new constructor[([arguments])]
 - 這個新建構的物件 this 會指向新建構的物件
 
1  | let x = 2;  | 
DOM
- 元素觸發事件,this 就指向那個元素
 - DOM 調用 function 就如同物件調用 function,所以此 this 所指向的則是該 DOM
 
1  | function doAlert() {  | 
比較優先順序
- 顯式綁定和建構式綁定無法直接比較(會報錯)
 - 匹配的優先順序由 高至低排列
- 建構式綁定:this 會指向 new 出來的物件
 - 顯示綁定:使用 call、apply、bind,明確指出要綁定給 this 的物件
 - 隱式綁定:當函式為物件的方法(method)時,在執行階段 this 就會被綁定至該物件
 - 預設綁定:當其他規則都不適用時,沒有使用 bind、call、apply 或不屬於任何物件的 method,就套用預設綁定,在非嚴格下,瀏覽器環境 this 的值就是預設值
全域物件 window,而在嚴格模式下,this 的值就是undefined 
 
結論
- this 代表的是 function 執行時所屬的物件
 - 判斷函數被調用時 this 指向誰,立馬看 ( )左邊的部分
 - function 內用嚴格模式下,默認的 this 就是 undefined
 - 可由 let self = this , let that = this 來設定變數取代 this
 - call、apply、bind 明確(強制)指定要執行 function 中的 this 是什麼,不用設定變數
 - new 綁定這個新建構的物件 this 會指向新建構的物件
 - DOM 元素觸發事件,this 就指向那個元素
 
| 執行方式 | 範例語法 | this等於 | 
|---|---|---|
| Global | this | Global object (eg. window) | 
| Global Function | foo() | Global object | 
| Object Function | myObject.foo() | myObject | 
| Function using call | foo.call(myCall,myArg) | myCall | 
| Function using apply | foo.apply(myCall,[myArgs]) | myApply | 
| Constructor Function | var newObj = new foo() | newObj | 
| Evaluation | eval(thing_to_eval) | 等同eval層級 | 
| 顯式綁定 | bind | call | apply | 
|---|---|---|---|
| 適用狀況 | 在執行前先綁定物件做為該 function 的 this,需要時才被呼叫不會立即執行 | 依照呼叫時的需要帶入不同的物件作為該 function 的 this,在呼叫的當下就立即執行 | 與 call 相同 | 
| 傳參數的方式 | 代入指定的物件 func.bind (thisArg[, arg1[, arg2[, ...]]]) | 
參數需要一個一個指定 function.call (thisArg, arg1, arg2, ...) | 
參數使用陣列傳入 function.apply (thisArg, [argsArray]) | 
element.addEventListener 中使用 this & e.target & e.currentTarget 差異
- this 總是會拿到被監聽的對象本身,也就是 element.addEventListener(
) 的 element  - e.currentTarget === this
 - e.target 則是指事件被觸發時的對象,有可能不是 element 本身
 
1  | <div class="hero">  |