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ì?
window
browser
undefined