Javascript – Truy cập đúng biến ‘this’ trong callback trả về

Những gì bạn nên biết về this

this (hay 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 / khi / nơi nó được định nghĩa. 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 this, hãy xem tài liệu MDN .

Cách lấy đúng ‘this’

Sử dụng Arrow Function

ECMAScript 6 đã giới thiệu các arrow function , có thể được coi là các hàm lambda. Họ không có this ràng buộc riêng của họ . Thay vào đó, this được tra cứu 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 cần phải gọi .bind. Đó không phải là hành vi đặc biệt duy nhất mà 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));
}

Không sử 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 đơn giản là tạo một biến mới cũng tham chiếu đến đối tượng đó. Biến có thể có bất kỳ tên nào, nhưng những tên phổ biến 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 bên trong lệnh gọi lại. Điều này cũng có lợi thế là bạn có thể truy cập this giá trị của chính lệnh gọi lại.

Xem thêm   So sánh TypeScript với JavaScript

Tập hợp rõ ràng this của callback – phần 1

Có vẻ như bạn không kiểm soát được giá trị của 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 đều có phương thức .bind  , phương thức này trả về một hàm mới có this giới hạn cho một giá trị. Hàm có hành vi giống hệt như hàm mà bạn đã gọi .bind, chỉ khác this là do bạn thiết lập. Bất kể hàm đó được gọi như thế nào hoặc khi nào, this sẽ luôn tham chiếu đến giá trị được truyền vào.

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 tôi đang ràng buộc callback’s this với giá trị của MyConstructor‘s this.

Lưu ý: Khi một ngữ cảnh ràng buộc cho jQuery, hãy sử dụng jQuery.proxy thay thế. Lý do để làm điều này là vì bạn không cần phải 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ộ điều đó.

Đặt this của callback – phần 2

Một số hàm / phương thức chấp nhận lệnh gọi lại cũng chấp nhận giá trị mà lệnh gọi lại this phải tham chiếu đến. Điều này về cơ bản giống như tự ràng buộc nó, nhưng hàm / phương thức sẽ làm điều đó cho bạn. Array#map  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à lệnh gọi lại và đối số thứ hai là giá trị this nên tham chiếu đến. Đâ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ể chuyển một giá trị cho this hay không thường được đề cập trong tài liệu của hàm / phương thức đó. Ví dụ: phương thức của jQuery $.ajax mô tả một tùy chọn được gọi là context:

Đối tượng này sẽ trở thành bối cảnh của tất cả các lệnh gọi lại liên quan đến Ajax.

Sự cố thường gặp: Sử dụng các phương thức đối tượng làm trình xử lý gọi lại / 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ý callback / event. Các hàm là first-class 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 hàm đó không có liên kết cụ thể đến đối tượng “chứa” của nó.

Xem thêm   Coding convention, Coding Standard cho Javascript

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 chuột, nhưng nếu document.body được nhấp, giá trị được ghi lại sẽ là undefined, bởi vì bên trong trình xử lý sự kiện, this tham chiếu đến document.body, không phải trường hợp của Foo.

Như đã đề cập ở phần đầu, những gì this đề cập đến phụ thuộc vào cách hàm được gọi , chứ không phải cách nó được định nghĩa .

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ó, hãy sử dụng .bind để liên kết rõ ràng thisvới một giá trị cụ thể

document.body.onclick = this.method.bind(this);

hoặc gọi hàm một cách rõ ràng là một “phương thức” của đối tượng, bằng cách sử dụng một 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 hàm mũi tên:

document.body.onclick = () => this.method();

Tham khảo: https://stackoverflow.com/

Viết một bình luận