Từ khóa này

Từ khoá this đề cập đến giá trị của đối tượng được liên kết với hàm tại thời điểm gọi hàm, nghĩa là giá trị của đối tượng này sẽ khác nhau tuỳ thuộc vào việc hàm được gọi dưới dạng phương thức, hàm độc lập hay hàm khởi tạo.

Khi được gọi, hàm sẽ tạo một thực thể của từ khoá this ở chế độ nền dưới dạng tham chiếu đến đối tượng chứa hàm đó, cho phép truy cập vào các thuộc tính và phương thức được xác định cùng với hàm đó trong phạm vi của hàm. Việc làm việc với this tương tự như việc làm việc với một biến được khai báo bằng const. Giống như một hằng số, bạn không thể xoá this và không thể chỉ định lại giá trị của hằng số này, nhưng bạn có thể thay đổi các phương thức và thuộc tính của đối tượng mà từ khoá this chứa.

Liên kết toàn cục

Bên ngoài một hàm hoặc ngữ cảnh của một đối tượng, this tham chiếu đến thuộc tính globalThis, là một tham chiếu đến đối tượng toàn cục trong hầu hết các môi trường JavaScript. Trong ngữ cảnh của một tập lệnh chạy trong trình duyệt web, đối tượng toàn cục là đối tượng window:

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

Trong Node.js, globalThis là đối tượng global:

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

Bên ngoài chế độ nghiêm ngặt, this cũng đề cập đến đối tượng toàn cục bên trong một hàm độc lập, vì Window mẹ là đối tượng "sở hữu" hiệu quả các hàm đó.

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

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

Khi sử dụng chế độ nghiêm ngặt, this có giá trị là undefined bên trong một hàm độc lập:

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

Trước khi giới thiệu chế độ nghiêm ngặt, giá trị null hoặc undefined cho this sẽ được thay thế bằng tham chiếu đến đối tượng toàn cục. Đôi khi, bạn có thể thấy tính năng liên kết toàn cục được gọi là "liên kết mặc định" do hành vi cũ này.

Liên kết ngầm ẩn

Khi một hàm được gọi là phương thức của một đối tượng, một thực thể của this bên trong phương thức đó sẽ tham chiếu đến đối tượng chứa phương thức, cấp quyền truy cập vào các phương thức và thuộc tính đi kèm với phương thức đó:

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

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

Có vẻ như giá trị của this phụ thuộc vào cách xác định hàm và đối tượng bao bọc của hàm đó. Thay vào đó, ngữ cảnh cho giá trị của this là ngữ cảnh thực thi hiện tại. Trong trường hợp này, ngữ cảnh thực thi là đối tượng myObject đang gọi phương thức myMethod, vì vậy myObject là giá trị cho this. Điều này có vẻ như là một vấn đề kỹ thuật trong ngữ cảnh của các ví dụ trước, nhưng đối với các trường hợp sử dụng nâng cao hơn của this, đây là một điểm khác biệt quan trọng cần lưu ý.

Nhìn chung, hãy sử dụng this theo cách không mong đợi mã xung quanh có bất kỳ cấu trúc cụ thể nào. Trường hợp ngoại lệ đối với quy tắc này là Hàm mũi tên ES5.

this trong hàm mũi tên

Trong hàm mũi tên, this phân giải thành một liên kết trong môi trường bao bọc theo từ vựng. Điều này có nghĩa là this trong hàm mũi tên tham chiếu đến giá trị của this trong ngữ cảnh bao gồm gần nhất của hàm đó:

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 {...}

Trong ví dụ trước, myObject.myMethod() ghi lại myObject dưới dạng đối tượng "sở hữu" phương thức đó, nhưng myObject.myArrowFunction() trả về globalThis (hoặc undefined), vì thực thể của this bên trong hàm mũi tên tham chiếu đến phạm vi bao bọc cao nhất.

Trong ví dụ sau, myEnclosingMethod tạo một hàm mũi tên trên đối tượng chứa hàm đó khi được thực thi. Bây giờ, thực thể của this bên trong hàm mũi tên sẽ tham chiếu đến giá trị của this bên trong môi trường bao bọc, đây là phương thức chứa hàm mũi tên đó. Vì giá trị của this bên trong myEnclosingMethod tham chiếu đến myObject, nên sau khi bạn xác định hàm mũi tên, this bên trong hàm mũi tên cũng tham chiếu đến myObject:

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

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

Liên kết tường minh

Liên kết ngầm ẩn xử lý hầu hết các trường hợp sử dụng để làm việc với this. Tuy nhiên, đôi khi bạn có thể cần giá trị của this để biểu thị ngữ cảnh thực thi cụ thể, thay vì ngữ cảnh giả định. Một ví dụ minh hoạ (nếu hơi lỗi thời) là làm việc với this trong hàm callback của setTimeout, vì lệnh gọi lại này có một ngữ cảnh thực thi riêng biệt:

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

setTimeout( myObject.myMethod, 100 );
> undefined

Mặc dù các tính năng khác đã giải quyết được điểm yếu cụ thể này của setTimeout, nhưng các vấn đề tương tự về việc "mất" this trước đây đã được giải quyết bằng cách tạo một tệp đối chiếu rõ ràng đến giá trị của this trong phạm vi ngữ cảnh dự kiến. Đôi khi, bạn có thể thấy các thực thể của this được gán cho một biến bằng cách sử dụng giá trị nhận dạng như that, self hoặc _this trong các cơ sở mã cũ. Đây là các quy ước nhận dạng phổ biến cho các biến chứa giá trị this đã truyền.

Khi bạn gọi một hàm bằng phương thức call(), bind() hoặc apply(), this sẽ tham chiếu rõ ràng đến đối tượng đang được gọi:

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."

Liên kết tường minh ghi đè giá trị this do liên kết ngầm cung cấp.

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."

Nếu một hàm được gọi theo cách đặt giá trị của this thành undefined hoặc null, thì giá trị đó sẽ được thay thế bằng globalThis bên ngoài chế độ nghiêm ngặt:

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

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

Tương tự, nếu một hàm được gọi theo cách cung cấp cho this một giá trị nguyên thuỷ, thì giá trị đó sẽ được thay thế bằng đối tượng trình bao bọc của giá trị nguyên thuỷ bên ngoài chế độ nghiêm ngặt:

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

let myNumber = 10;

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

Ở chế độ nghiêm ngặt, giá trị this đã truyền không bị ép buộc thành đối tượng theo bất kỳ cách nào, ngay cả khi đó là giá trị gốc, null hoặc undefined:

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

let myNumber = 10;

myFunction.call( myNumber );
> 10

myFunction.call( null );
> null

Liên kết new

Khi một lớp được dùng làm hàm khởi tạo bằng từ khoá new, this sẽ tham chiếu đến thực thể mới tạo:

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

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

Tương tự, giá trị của this bên trong hàm khởi tạo được gọi bằng new tham chiếu đến đối tượng đang được tạo:

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

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

Liên kết trình xử lý sự kiện

Trong ngữ cảnh của trình xử lý sự kiện, giá trị của this tham chiếu đến đối tượng gọi sự kiện đó. Bên trong hàm gọi lại của trình xử lý sự kiện, điều đó có nghĩa là this tham chiếu đến phần tử được liên kết với trình xử lý:

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

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

Khi người dùng tương tác với button trong đoạn mã trước, kết quả sẽ là đối tượng phần tử chứa chính <button>:

> Button {}

Khi một hàm mũi tên được dùng làm lệnh gọi lại trình nghe sự kiện, giá trị của this lại được cung cấp bởi ngữ cảnh thực thi bao bọc gần nhất. Ở cấp cao nhất, tức là this bên trong hàm gọi lại của trình xử lý sự kiện là globalThis:

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

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

Giống như mọi đối tượng khác, khi bạn sử dụng các phương thức call(), bind() hoặc apply() để tham chiếu hàm gọi lại của trình nghe sự kiện, this sẽ tham chiếu đối tượng một cách rõ ràng:

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

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

Kiểm tra mức độ hiểu biết

Đối với một tập lệnh chạy trong trình duyệt web, đối tượng toàn cục mà this tham chiếu đến khi được sử dụng bên ngoài một hàm hoặc ngữ cảnh của một đối tượng là gì?

Đối tượng window
Đối tượng browser
Đối tượng undefined