前言

今天要來講的主題是this,在JavaScript中的this可以在呼叫函式時,透過不同方式決定它要指向哪一個物件,而關於什麼時候會指向什麼地方,這就是this的困難之處。

理解this來由

一開始自己的猜測理解是,有些很冗長的名字,假如有一個代名詞,去代替它會很好很多。

比如說,我要描述在一家便利商店打工的小白擁有的東西:

在便利商店打工的小白擁有著一台機車,在便利商店打工的小白還有隨身攜帶一個水壺,而且在便利商店打工的小白手中現在還拿著一個過期的超商飯糰。

假如把「便利商店打工的小白」用「他」來替換掉的話:

在便利商店打工的小白擁有著一台機車,「他」還有隨身攜帶一個水壺,而且「他」手中現在還拿著一個過期的超商飯糰。

程式碼寫起來概念會像這樣:

這邊變數名稱就不要太長單純是white就好,
但可以想像成叫做SuperWhiteWorkingInAConvenienceStore比較符合概念。

1
2
3
4
5
6
7
8
9
10
11
const white = {
transportation: "motorcycle",
thing: "kettle",
describe: function () {
console.log(
"小白擁有一台" + white.transportation + ",小白還隨身攜帶" + white.thing
);
},
};

white.describe(); // 小白擁有一台motorcycle,小白還隨身攜帶kettle

程式加上了this的概念會變成這樣:

1
2
3
4
5
6
7
8
9
10
11
const white = {
transportation: "motorcycle",
thing: "kettle",
describe: function () {
console.log(
"小白擁有一台" + this.transportation + ",小白還隨身攜帶" + this.thing
);
},
};

white.describe(); //小白擁有一台motorcycle,小白還隨身攜帶kettle

這兩種結果都是一樣的,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的時候 生命週期包括三個階段。

建立階段 → 執行階段 → 回收階段

而在建立階段會去做三件事情:

  1. 確定 this 的值,也被稱為 This Binding
  2. LexicalEnvironment(詞法環境) 元件被建立
  3. VariableEnvironment(變數環境) 元件被建立

code 大致長這樣=>

1
2
3
4
5
ExecutionContext = {
ThisBinding = <this value>, // 確定this
LexicalEnvironment = { ... }, // 詞法環境
VariableEnvironment = { ... }, // 變數環境
}

所以說 ThisBinding 是和Execution Context綁定的,也就是說每個執行環境都有一個 this,與 es3this 並沒有什麼區別,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
2
3
4
5
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有綁定指向的四個招式來解釋:

  • Default Binding (預設綁定)
  • Implicit Binding (隱含綁定)
  • Explicit Binding (顯示綁定)
  • new Binding (new 綁定)

然後第一種的預設綁定已經講完了,就是在沒意義的狀況下,this會有預設的值。

接下來講其他三種。

隱含式綁定 (Implicit Binding)

關鍵字:透過 .

1
2
3
4
5
6
7
var vic = {
name: "vic",
getName: function () {
return this.name;
},
};
vic.getName(); // vic

隱含就是沒有明確說出this正式綁定的對象是什麼,用一個.可以取出物件屬性的特性,同時也告知this是可以指向哪裡。

最前面介紹小白擁有東西的程式就是利用Implicit Binding來綁定this

顯式綁定 (Explicit Binding)

關鍵字: 透過call apply bind

1
2
3
4
5
6
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,也依然會被這種方法給覆蓋掉。

1
2
3
4
5
6
function main() {
console.log(this);
}

const Mainmain = main.bind("test");
Mainmain(); // main

bind 會回傳一個新的 function,這邊把 main 這個 function 用 test 來綁定。

「new」關鍵字綁定

關鍵字: 透過new

1
2
3
4
5
6
7
8
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 來創造物件比較不常見

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] 帶你徹底搞懂執行上下文