前言
接續上一篇的 this
,我們將利用一種比較特別的方式來看 this
的值,以及介紹除了 call()
、apply()
之外還可以使用 bind()
強制綁定 this
,最後將提到 this
在箭頭函式下的特性。
用另一種角度看 this 的值
上一節提到 this
基本只有在跟物件導向扯上關係的時候才有意義。
1 | use strict' |
而這個例子的輸出結果 this
指向的是 obj
。
有沒有發現幾乎每次
this
有改變的時候都是ooo.xxx()
之類的呼叫方式
this 的值與在哪邊呼叫函式、實際上寫在哪無關
- 真正有關係的是,如何去呼叫它
同一個例子換個方式改寫結果就不同了
1 | 'use strict' |
這個例子就是在說,明明是同樣的輸出結果,但只要改變了呼叫的方式, this
就不一樣了。
是不是覺得要判斷 this 值是什麼有點困難?
可以試著帶入 call()
的方式去想!
1 | 'use strict' |
透過帶入 call() 的方式去想將有助於理解 this 的值是什麼。
以 obj.test()
來說,可以想像成在 obj.test()
後面補上 call()
並且填入呼叫 test()
之前的內容,就是 this
指向的地方。
在來個例子
1 | 'use strict' |
一樣使用上面提到的方式去判斷,因此可以得知結果的 this
會指向 obj.inner
。
所以現在就可以了解,第一個例子為什麼換個方式改寫輸出會是 undefined 了。
1 | 'use strict' |
- 因為 func 前面沒有任何東西可以放入
call()
,所以會指向undefined
做點小練習題複習一下 this
吧
1 | function log() { |
bind() 讓 this 從此乖乖的
上一節介紹的 call()
、 apply()
主要是呼叫該函式,並指定 this
值的指向。但是 bind()
並不一樣:
bind()
會回傳一個一模一樣的函式,並且將 this 值強制綁定而且沒有辦法透過
call()
、apply()
改變this
值1
2
3
4
5
6
7
8
9
10
11'use strict'
const obj = {
a: 123,
test: function(){
console.log(this);
}
}
const bindTest = obj.test.bind('aaa');
bindTest(); // aaa
bindTest.call('qweqwe'); // aaabind()
的用法其實跟前面兩個蠻接近的,第一個參數都是指定this
this 在箭頭函式下的特性
在介紹之前做個小練習
1 | class Test{ |
輸出的 log 分別為:
- Test{}
- 全域 window 物件
但如果將 setTimeout 內的函式改成箭頭函式呢?
1 | class Test{ |
答案就會很明顯的不一樣囉。
與 this 的特性相反
- this 取決於函式如何被呼叫
但箭頭函式內的 this
- 跟函式怎麼被呼叫沒有關係
- 表現跟 scope 的行為比較類似,取決於箭頭函式寫在哪
回到例子
箭頭函式內的 this
取決於被寫在哪,來決定 this
是什麼。
以這個例子來說,因為這個方法是這樣被呼叫的 t.run()
,所以 run 函式內的 this
會指向 t
,而同樣在 run
函式中的箭頭函式 this
也會跟著變成 t
。
而我們也可以透過這樣來觀察
1 | class Test{ |
當我們指定 this
後,也會連帶的影響到箭頭函式內的 this
。
也就是說箭頭函式內的 this
會是在被定義時那個區域的 this
值。
1 | function test(){ |
像是這個例子來說,如果沒有使用 call()
指定 this
,這邊印出的 this
值會全部都是全域 window
,但因為現在有指定 this
,所以全部都是 ‘aa’ 。
心得
總算是把支線部分全部都讀完了,真的是對我的 JavaScript 底層的理解相當的有幫助,並且也把一些在奇怪部分 (我以為我懂但其實沒有) 的觀念釐清,像是我很喜歡模仿 JavaScript 引擎、找作用域、從 ECMA 理解 hosting 那些方式,像是偵探在找線索般,一層層的往回推,最後就可以理解為什麼會是這樣。
接下來就是把剩下的主線完成, JavaScript 的坑就算完成啦。