一次搞懂 JavaScript 的 this:簡單實用的指南
前言
在 JavaScript 的世界裡,有一個讓新手困惑、讓老手心生畏懼的主題:this 到底是什麼?
「為什麼這個函式裡的
this是undefined?」 「物件的方法好好的,為什麼this突然變成了window?」 「箭頭函式的this跟普通函式不一樣?到底要怎麼判斷?」
如果你曾在開發中遇 到這樣的問題,我想對你說:你並不孤單😿
雖然網路上已經有很多介紹 this 的文章,我可能也不會寫得比其他文章還深入,但我還是想要以我目前對 this 的理解程度,站在我的視角,來分享我是如何理解 this 的。當然,這篇文章不會「完全」解釋所有 this 的邏輯,畢竟要徹底理解它,你還得翻開 ECMAScript 規範才行。但我保證,這篇文章能夠幫助你在大多數情況下快速判斷 this,甚至對 this 有更深一層的認識。
這篇文章適合 對 JavaScript 有一定基礎 的開發者。文章內容將會包括:
- JavaScript 的 基本語法(如物件、函式、
class等)。 - JavaScript 的 作用域 和 閉包 是什麼(沒關係,這裡也會簡單提到)。
如果這些你還不太熟悉,建議先補充相關知識,否則讀到後面你可能會更加混亂。
為什麼 JS 的 this 這麼難懂 ?
其實,JavaScript 的 this 之所以讓人困惑,是因為它和其他物件導向語言的 this 有所不同:
- 它可以脫離物件被呼叫。
- 它可以被手動改變
this的指向(例如call、apply和bind)。 - 它還有箭頭函式這種特殊存在,會繼承外部作用域的
this。
JavaScript 的 this 最大的特點在於:
this的值並不是在「函式定義時」決定的,而是在「函式執行時」根據呼叫方式動態決定的。
這種彈性雖然賦予了 JavaScript 很大的自由度,但也讓 this 變得複雜且容易出錯。
this 的本質:物件導向的延伸
在大多數物件導向語言中,this 從來都不是什麼難懂的概念。它的存在非常單純:它代表當前實例(instance)本身,方便在類別內存取物件的屬性或方法。但在 JavaScript 裡面,好像並沒有這麼單純。
物件導向語言裡的 this
我們先來看看一個簡單的例子:
class Car {
setName(name) {
this.name = name; // this 指向當前的實例
}
getName() {
return this.name;
}
}
const myCar = new Car();
myCar.setName('Tesla');
console.log(myCar.getName()); // Tesla
在這段程式碼中,this 的存在是必需的,因為我們需要一個方式來指代當前物件的屬性或方法。
this.name = name:表示把傳進來的name設定到當前實例 的name屬性上。myCar.setName('Tesla')呼叫時,this指向myCar,所以this.name實際上是myCar.name。
這種寫法在物件導向語言中是非常直觀的,因為 this 就是物件自己的「代名詞」。
脫離物件導向的 this
然而,在 JavaScript 中,this 並不局限於物件或類別內,它可以出現在任何地方!
- 在函式中,
this可能是全域物件(window或global)。 - 在事件處理函式中,
this指向觸發事件的元素。 - 在
setTimeout、箭頭函式等情境下,this的行為又不一樣了。
我們來看一個例子:
function hello() {
console.log(this);
}
hello();
你覺得這裡的 this 是什麼?
在其他語言中,這段程式碼可能根本不成立,因為 this 只有在類別或物件內才有意義。但在 JavaScript 裡,this 會根據執行環境給出一個預設值:
- 非嚴格模式:
this指向 全域物件(瀏覽器中是window,Node.js 是global)。 - 嚴格模式:
this是undefined。
"use strict";
function hello() {
console.log(this);
}
hello(); // undefined
當 this 脫離物件,並單純存在於一般函式中時,它其實沒有什麼太大的意義,僅僅是語言機制給了一個預設值罷了。
(引用自 @淺談 JavaScript 頭號難題 this:絕對不完整,但保證好懂):
this的指向規則
前面我們有提到,在 JavaScript 中,this 的值不是在函式定義時決定的,而是根據 「函式執行時的呼叫方式」 動態決定的,這是理解 this 的核心關鍵。在判斷 this 值時,我們需要時刻記住一個核心原則:
要看
this,就看「誰,在哪裡呼叫了這個函式」
全域環境中的 this
我們先從最簡單的情況開始:在全域環境中 this 的值是什麼?
範例:
console.log(this);
結果:
- 瀏覽器環境:
this指向window物件。 - Node.js 環境:
this指向global物件(模組作用域則是{})。
一般函式中的 this
當函式不是物件方法,而 是單獨呼叫時,this 的值取決於是否處於「嚴格模式」。
範例:
function hello() {
console.log(this);
}
hello(); // 非嚴格模式
結果:
- 非嚴格模式:
this指向全域物件(window或global)。 - 嚴格模式:
this是undefined。
嚴格模式範例:
"use strict";
function hello() {
console.log(this);
}
hello(); // undefined
小結:普通函式中的 this 取決於是否嚴格模式。如果脫離物件,this 基本上沒有意義,只會回傳預設值。
物件方法中的 this
當函式作為「物件的方法」被呼叫時,this 指向呼叫該方法的物件。
範例:
const obj = {
name: 'Alice',
sayName() {
console.log(this.name);
}
};
obj.sayName();