Những gì bạn nên biết về this
this
(còn gọi là "bối cảnh") là một từ khóa đặc biệt bên trong mỗi hàm và giá trị của nó chỉ phụ thuộc vào cách hàm được gọi, chứ không phải cách thức / thời gian / nơi nó được xác định. Nó không bị ảnh hưởng bởi phạm vi từ vựng như các biến khác (ngoại trừ các hàm mũi tên, xem bên dưới). Dưới đây là một số ví dụ:
function foo() {
console.log(this);
}
// normal function call
foo(); // `this` will refer to `window`
// as object method
var obj = {bar: foo};
obj.bar(); // `this` will refer to `obj`
// as constructor function
new foo(); // `this` will refer to an object that inherits from `foo.prototype`
Để tìm hiểu thêm về this
, hãy xem tài liệu MDN .
Làm thế nào để tham khảo chính xác this
Đừng dùng this
Bạn thực sự không muốn truy cập this
cụ thể, nhưng đối tượng mà nó đề cập đến . Đó là lý do tại sao một giải pháp dễ dàng là chỉ cần tạo một biến mới cũng đề cập đến đối tượng đó. Biến có thể có bất kỳ tên nào, nhưng tên chung là self
và that
.
function MyConstructor(data, transport) {
this.data = data;
var self = this;
transport.on('data', function() {
alert(self.data);
});
}
Vì self
là một biến bình thường, nó tuân theo các quy tắc phạm vi từ vựng và có thể truy cập được trong cuộc gọi lại. Điều này cũng có lợi thế là bạn có thể truy cập vào this
giá trị của chính cuộc gọi lại.
Hoàn toàn thiết lập this
cuộc gọi lại - phần 1
Có vẻ như bạn không có quyền kiểm soát giá trị this
vì giá trị của nó được đặt tự động, nhưng thực tế không phải vậy.
Mỗi hàm có phương thức .bind
[docs] , trả về một hàm mới có this
ràng buộc với một giá trị. Hàm này có chính xác hành vi giống như hành vi bạn đã gọi .bind
, chỉ có điều đó this
được đặt bởi bạn. Bất kể chức năng đó được gọi như thế nào hoặc khi nào, this
sẽ luôn đề cập đến giá trị được truyền.
function MyConstructor(data, transport) {
this.data = data;
var boundFunction = (function() { // parenthesis are not necessary
alert(this.data); // but might improve readability
}).bind(this); // <- here we are calling `.bind()`
transport.on('data', boundFunction);
}
Trong trường hợp này, chúng ta đang ràng buộc gọi lại của this
giá trị của MyConstructor
's this
.
Lưu ý: Khi liên kết ngữ cảnh cho jQuery, thay vào đó hãy sử dụng jQuery.proxy
[docs] . Lý do để làm điều này là vì vậy bạn không cần lưu trữ tham chiếu đến hàm khi hủy liên kết một cuộc gọi lại sự kiện. jQuery xử lý nội bộ đó.
ECMAScript 6 giới thiệu các hàm mũi tên , có thể được coi là các hàm lambda. Họ không có this
ràng buộc riêng . Thay vào đó, this
được tìm kiếm trong phạm vi giống như một biến bình thường. Điều đó có nghĩa là bạn không phải gọi .bind
. Đó không phải là hành vi đặc biệt duy nhất họ có, vui lòng tham khảo tài liệu MDN để biết thêm thông tin.
function MyConstructor(data, transport) {
this.data = data;
transport.on('data', () => alert(this.data));
}
Đặt this
cuộc gọi lại - phần 2
Một số chức năng / phương thức chấp nhận cuộc gọi lại cũng chấp nhận giá trị mà cuộc gọi lại this
nên tham khảo. Điều này về cơ bản giống như tự ràng buộc nó, nhưng chức năng / phương thức thực hiện nó cho bạn. Array#map
[docs] là một phương pháp như vậy. Chữ ký của nó là:
array.map(callback[, thisArg])
Đối số đầu tiên là cuộc gọi lại và đối số thứ hai là giá trị this
nên tham khảo. Đây là một ví dụ giả định:
var arr = [1, 2, 3];
var obj = {multiplier: 42};
var new_arr = arr.map(function(v) {
return v * this.multiplier;
}, obj); // <- here we are passing `obj` as second argument
Lưu ý: Việc bạn có thể vượt qua một giá trị hay không this
thường được đề cập trong tài liệu về chức năng / phương thức đó. Ví dụ: phương thức [docs] của jQuery$.ajax
mô tả một tùy chọn được gọi là context
:
Đối tượng này sẽ được tạo thành bối cảnh của tất cả các cuộc gọi lại liên quan đến Ajax.
Vấn đề thường gặp: Sử dụng các phương thức đối tượng làm hàm gọi lại / xử lý sự kiện
Một biểu hiện phổ biến khác của vấn đề này là khi một phương thức đối tượng được sử dụng làm trình xử lý gọi lại / sự kiện. Các hàm là các công dân hạng nhất trong JavaScript và thuật ngữ "phương thức" chỉ là một thuật ngữ thông tục cho một hàm là giá trị của một thuộc tính đối tượng. Nhưng chức năng đó không có một liên kết cụ thể đến đối tượng "chứa" của nó.
Hãy xem xét ví dụ sau:
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = function() {
console.log(this.data);
};
Hàm this.method
được gán là trình xử lý sự kiện nhấp, nhưng nếu document.body
được bấm, giá trị được ghi sẽ là undefined
, bởi vì bên trong trình xử lý sự kiện, this
đề cập đến document.body
, chứ không phải là thể hiện của Foo
.
Như đã đề cập ở phần đầu, những gì this
đề cập đến phụ thuộc vào cách gọi hàm , chứ không phải cách xác định .
Nếu mã giống như sau, có thể rõ ràng hơn là hàm không có tham chiếu ngầm đến đối tượng:
function method() {
console.log(this.data);
}
function Foo() {
this.data = 42,
document.body.onclick = this.method;
}
Foo.prototype.method = method;
Giải pháp tương tự như đã đề cập ở trên: Nếu có sẵn, hãy sử dụng .bind
để liên kết rõ ràng this
với một giá trị cụ thể
document.body.onclick = this.method.bind(this);
hoặc gọi một cách rõ ràng hàm là "phương thức" của đối tượng, bằng cách sử dụng hàm ẩn danh làm trình xử lý gọi lại / sự kiện và gán đối tượng ( this
) cho một biến khác:
var self = this;
document.body.onclick = function() {
self.method();
};
hoặc sử dụng chức năng mũi tên:
document.body.onclick = () => this.method();