此關鍵字

關鍵字 this 是指呼叫時繫結至函式的物件值,意即該函式的值會因函式稱為方法、獨立函式或建構函式而有所不同。

呼叫函式時,它會在背景建立關鍵字 this 的執行個體,做為包含該函式之物件的參照,從範圍中存取與此函式一起定義的屬性和方法。使用 this 的方式與使用 const 宣告的變數有些類似。如同常數,this 無法移除且其值無法重新指派,但 this 關鍵字包含的物件方法和屬性可以修改。

全域繫結

在函式或物件結構定義外,this 是指 globalThis 屬性,這是大多數 JavaScript 環境中全域物件的參照。在網路瀏覽器中執行的指令碼中,全域物件為 window 物件:

this;
> Window {0: Window, window: Window, self: Window, document: document, name: '', location: Location, ...}

在 Node.js 中,globalThisglobal 物件:

$ node
Welcome to Node.js v20.10.0.
Type ".help" for more information.
> this
<ref *1> Object [global] {
...
}

在外部嚴格模式下,this 也會參照獨立函式中的全域物件,因為父項 Window 是有效「擁有」這些函式的物件。

function myFunction() {
    console.log( this );
}
myFunction();
> Window {...}

(function() {
    console.log( this );
}());
> Window {...}

使用嚴格模式時,this 在獨立函式中具有 undefined 的值:

(function() {
    "use strict";
    console.log( this );
}());
> undefined

在嚴格模式推出前,thisnullundefined 值會替換為全域物件的參照。由於這個舊版行為,有時您可能會看見全域繫結稱為「預設繫結」。

隱含繫結

當系統呼叫函式做為物件方法時,該方法中的 this 例項會參照包含該方法的物件,進而授予附帶該方法之方法和屬性的存取權:

let myObject = {
    myValue: "This is my string.",
    myMethod() {
            console.log( this.myValue );
    }
};

myObject.myMethod();
> "This is my string."

this 的值可能取決於函式及其所含物件的定義。相反地,this 值的結構定義是目前的「執行」背景資訊。在此情況下,執行作業背景為 myObject 物件呼叫 myMethod 方法,因此 myObjectthis 的值。以上範例看來似乎是技術層面,但對於 this 的更進階用途,您必須瞭解這一點。

一般來說,請以不預期周圍程式碼具有任何特定結構的方式使用 this。這項規則的例外狀況是 ES5 方向鍵

箭頭函式中的 this

狹窄函式中,this 會解析為字詞邏輯包覆環境中的繫結。也就是說,箭頭函式中的 this 是指在函式最接近背景資訊中的 this 值:

let myObject = {
    myMethod() { console.log( this ); },
    myArrowFunction: () => console.log( this ),
    myEnclosingMethod: function () {
        this.myArrowFunction = () => { console.log(this) };
    }
};

myObject.myMethod();
> Object { myMethod: myMethod(), myArrowFunction: myArrowFunction() }

myObject.myArrowFunction();
> Window {...}

在前例中,myObject.myMethod() 會將 myObject 記錄為「擁有」該方法的物件,但 myObject.myArrowFunction() 會傳回 globalThis (或 undefined),因為箭頭函式中的 this 例項參照的是最高的包含範圍。

在以下範例中,myEnclosingMethod 會在執行時,針對包含該函式的物件建立箭頭函式。箭頭函式中的 this 執行個體現在是指包圍環境內的 this 值,也就是包含該箭頭函式的方法。由於 myEnclosingMethod 中的 this 值指的是 myObject,因此在您定義箭頭函式後,箭頭函式中的 this 也會參照 myObject

let myObject = {
    myMethod() { console.log( this ); },
    myEnclosingMethod: function () {
        this.myArrowFunction = () => { console.log(this) };
    }
};

myObject.myEnclosingMethod();
myObject.myArrowFunction();
> Object { myMethod: myMethod(), myArrowFunction: myArrowFunction() }

明確繫結

隱式繫結可處理大多數與 this 相關的用途。然而,有時您可能需要 this 的值代表「特定」的執行內容,而非假設的結構定義。如果稍微過時,表示此回呼在 setTimeout 的回呼函式中如何與 this 搭配使用,因為這個回呼具有不重複的執行結構定義:

var myObject = {
  myString: "This is my string.",
  myMethod() {
    console.log( this.myString );
  }
};
myObject.myMethod();
> "This is my string."

setTimeout( myObject.myMethod, 100 );
> undefined

雖然其他功能已解決這個 setTimeout 的特定不足問題,但先前「遺失」this 的問題先前已透過在預期情境的範圍內建立對 this 值的明確參照來解決。有時候,您可能會在舊版程式碼集中使用 thatself_this 等 ID,將 this 的執行個體指派給變數。對於包含傳遞的 this 值的變數,這些是常見的 ID 慣例。

使用 call()bind()apply() 方法呼叫函式時,this 會明確參照要呼叫的物件:

let myFunction = function() {
    console.log( this.myValue );
}

let myObject = {
   "myValue" : "This is my string."
 };

myFunction.call( myObject );
> "This is my string."
var myObject = {
  myString: "This is my string.",
  myMethod() {
    console.log( this.myString );
  }
};

setTimeout( myObject.myMethod.bind( myObject ), 100 );
> "This is my string."

明確繫結會覆寫隱含繫結提供的 this 值。

let myObject = {
    "myValue" : "This string sits alongside myMethod.",
    myMethod() {
        console.log( this.myValue );
    }
};
let myOtherObject = {
    "myValue" : "This is a string in another object entirely.",
};

myObject.myMethod.call( myOtherObject );
> "This is a string in another object entirely."

如果呼叫函式的方式會將 this 的值設為 undefinednull,則該值會替換為嚴格模式以外的 globalThis

let myFunction = function() {
    console.log( this );
}

myFunction.call( null );
> Window {...}

同樣地,如果呼叫函式的方式會提供原始值給 this,則該值會在嚴格模式之外取代原始值的包裝函式物件

let myFunction = function() {
    console.log( this );
}

let myNumber = 10;

myFunction.call( myNumber );
> Number { 10 }

在嚴格模式中,傳遞的 this 值不會以任何方式強制轉換為物件,即使它是原始值、nullundefined 值也一樣:

"use strict";
let myFunction = function() {
    console.log( this );
}

let myNumber = 10;

myFunction.call( myNumber );
> 10

myFunction.call( null );
> null

new 繫結

使用 new 關鍵字將類別當做建構函式使用時,this 會參照新建立的執行個體:

class MyClass {
    myString;
    constructor() {
        this.myString = "My string.";
    }
    logThis() {
        console.log( this );
    }
}
const thisClass = new MyClass();

thisClass.logThis();
> Object { myString: "My string." }

同樣地,在使用 new 呼叫的建構函式函式中,this 的值就是指要建立的物件:

function MyFunction() {
  this.myString = "My string.";
  this.logThis = function() {
    console.log( this );
  }
}
const myObject = new MyFunction();

myObject.logThis();
> Object { myString: "My string.", logThis: logThis() }

事件處理常式繫結

在事件處理常式的結構定義中,this 的值會參照叫用該處理常式的物件。在事件處理常式的回呼函式中,這表示 this 會參照與處理常式相關聯的元素:

let button = document.querySelector( "button" );

button.addEventListener( "click", function( event ) { console.log( this ); } );

當使用者在先前的程式碼片段中與 button 互動時,結果會是包含 <button> 的元素物件:

> Button {}

當箭頭函式做為事件監聽器回呼使用時,系統會再次由最接近的封閉執行結構定義提供 this 的值。頂層表示事件處理常式回呼函式中的 thisglobalThis (或嚴格模式下的 undefined):

let button = document.querySelector( "button" );

button.addEventListener( "click", ( event ) => { console.log( this ); } );
> undefined

和其他物件一樣,使用 call()bind()apply() 方法參照事件監聽器的回呼函式時,this 會明確參照物件:

let button = document.querySelector( "button" );
let myObject = {
    "myValue" : true
};
function handleClick() {
    console.log( this );
}

button.addEventListener( "click", handleClick.bind( myObject ) );
> Object { myValue: true }

隨堂測驗

如為在網路瀏覽器中執行的指令碼,this在函式外或物件情境下使用時,這個全域物件是什麼?

window 物件
browser 物件
undefined 物件