JS之路 Day20 - What is this?
前言
今天要來講的主題是this
,在JavaScript
中的this
可以在呼叫函式時,透過不同方式決定它要指向哪一個物件,而關於什麼時候會指向什麼地方,這就是this
的困難之處。
理解this
來由
一開始自己的猜測理解是,有些很冗長的名字,假如有一個代名詞,去代替它會很好很多。
比如說,我要描述在一家便利商店打工的小白擁有的東西:
在便利商店打工的小白擁有著一台機車,在便利商店打工的小白還有隨身攜帶一個水壺,而且在便利商店打工的小白手中現在還拿著一個過期的超商飯糰。
假如把「便利商店打工的小白」用「他」來替換掉的話:
在便利商店打工的小白擁有著一台機車,「他」還有隨身攜帶一個水壺,而且「他」手中現在還拿著一個過期的超商飯糰。
程式碼寫起來概念會像這樣:
這邊變數名稱就不要太長單純是white
就好,
但可以想像成叫做SuperWhiteWorkingInAConvenienceStore
比較符合概念。
1 | const white = { |
程式加上了this
的概念會變成這樣:
1 | const white = { |
這兩種結果都是一樣的,this
會去指向這個物件的變數名稱,不管呼叫的東西有很長,最終都只需要四個字母的this
就能取代掉,這是我一開始所對this
的理解。
用「這個」來當代名詞,利用「這個」來呼叫所指的對象。
然後去看看比較正確的定義是什麼,這裡推薦去看:
- ECMAScript 標準規範
- MDN - this 官方文件
先來看看 ECMAScript 標準規範 對 this 的定義:
「The this keyword evaluates to the value of the ThisBinding of the current execution context.」
翻中文:
「this 這個關鍵字代表的值為目前執行環境的 ThisBinding。」
好,我當初也看不太懂什麼是ThisBinding
。
ThisBinding 是什麼
有去研究了一下 有看到一篇文章有講述,整理一些重點。
文章是這篇:帶你徹底搞懂執行上下文
在es5 Execution Context
的時候 生命週期包括三個階段。
建立階段 → 執行階段 → 回收階段
而在建立階段會去做三件事情:
- 確定 this 的值,也被稱為 This Binding
- LexicalEnvironment(詞法環境) 元件被建立
- VariableEnvironment(變數環境) 元件被建立
code 大致長這樣=>
1 | ExecutionContext = { |
所以說 ThisBinding
是和Execution Context
綁定的,也就是說每個執行環境都有一個 this
,與 es3
的this
並沒有什麼區別,this
的值是在執行的時候才能確認,定義的時候不能確認。
可以理解成,一個新的函式執行環境被建立時,JavaScript
就會自動幫建立一個新的this
,所以這邊解開了一個盲點,不是呼叫了一個東西才創造this
,而是函式只要創建,就會創造執行環境,而執行環境的建立階段就會去做This Binding
的這個動作,確認有this
的值。
然後來看看 MDN 對 this 的定義:
「In most cases, the value of this is determined by how a function is called.」
翻中文:
「在大多數的情況下,this 會因為 function 被呼叫的方式而有所不同。」
意思是this
所指向的值,不是在被定義的當下決定的,是在被呼叫的時候決定的,這個部分會跟Scope Chain
完全反過來,而今天主要談論是this
,關於Scope Chain
的部分,以後會專門做一期文章為大家講解。
所以在這裡MDN
告訴了我們一件事情,隨著執行的方式不同,this
所指向的值也都會不同,而注意到了一個細節,這邊是講說In most cases
,在大多數的情況下,所以不是所有情況,這種狀況我理解是無意義的情況。
this 無意義的情況
無意義的情況是指物件之外的this
就沒有意義去討論,沒有意義的意思是,在這種狀況this
是什麼都沒關係,不重要。
比如說要去探討一個function
裡面的this
是會指向哪裡。
1 | const func = function () { |
會發現說在瀏覽器環境下會指向Window
的物件。
也不是指向func
這個function
,就像是我在介紹一個人,他很帥,什麼人? 世界上的某一個人,感覺沒有什麼意義。
這個指向的Window
物件也只是因為沒意義的狀況下給的預設值,通常會分成幾種狀況,我做了一個表格。
預設狀態下的this值
:
瀏覽器環境 | node.js 環境 | |
---|---|---|
嚴格模式 | undefined | undefined |
非嚴格模式 | window | global |
整理一下,MDN
講的In most cases
是指在物件導向內,this
所指的東西就是物件裡面的instance
,而只要脫離了物件導向,就不用去管this
的值是什麼,因為沒有意義。
而this
到底是如何覺得要怎麼指向哪裡,MDN
是說因為被呼叫的方式,
在這邊當作this
有綁定指向的四個招式來解釋:
- Default Binding (預設綁定)
- Implicit Binding (隱含綁定)
- Explicit Binding (顯示綁定)
- new Binding (new 綁定)
然後第一種的預設綁定已經講完了,就是在沒意義的狀況下,this
會有預設的值。
接下來講其他三種。
隱含式綁定 (Implicit Binding)
關鍵字:透過 .
1 | var vic = { |
隱含就是沒有明確說出this
正式綁定的對象是什麼,用一個.
可以取出物件屬性的特性,同時也告知this
是可以指向哪裡。
最前面介紹小白擁有東西的程式就是利用Implicit Binding
來綁定this
。
顯式綁定 (Explicit Binding)
關鍵字: 透過call
apply
bind
1 | function main(a, b) { |
你第一個參數傳什麼,裡面this
的值就會是什麼。儘管原本已經有 this
,也依然會被這種方法給覆蓋掉。
1 | function main() { |
bind 會回傳一個新的 function,這邊把 main 這個 function 用 test 來綁定。
「new」關鍵字綁定
關鍵字: 透過new
1 | function Vic(name) { |
當用 new 來建立一個先的物件,這個新的物件會被設為 this 綁定的目標。
最後做一個小小的綁定整理
vic 定義 | 補充 | |
---|---|---|
預設綁定 | this 指向外面沒東西 | function 被呼叫的當下如果沒有值,this 會自動指向全域 |
隱式綁定 | 透過 . 隱含指出 | 使用 . 可以取用到物件底下的屬性 |
顯式綁定 | call(), apply(), bind() | 直接強制給的,很明確 |
關鍵字綁定 | this 指定新 new 出的物件 | 透過 new 來創造物件比較不常見 |
reference
[1] MDN - this
[2] 淺談 JavaScript 頭號難題 this:絕對不完整,但保證好懂
[3] What’s THIS | 淺談 Javascript 中令人困擾的 this
[4] What’s THIS in JavaScript ? [上]
[5] JavaScript 深入之从 ECMAScript 规范解读 this
[6] JS 原力覺醒 Day17 - this 的四種繫結
[7] 帶你徹底搞懂執行上下文