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