Làm sao để sử dụng đúng giá trị của 'this' bên trong hàm callback?

Đối với những bạn mới học JavaScript thì this là một cái gì đó rất huyền bí. Và một trong những lỗi sai phổ biến, đó là sử dụng sai vai trò của this bên trong hàm callback.

Ví dụ:

class Helper {
  constructor() {
    this.number = 0;
  }

  increase() {
    setTimeout(function() {
      this.number++;
      console.log(this.number); // => NaN
    }, 100);
  }
}

const helper = new Helper();
helper.increase();

Trong đoạn code trên, console hiển thị giá trị NaN thay vì 1 - như suy nghĩ của hầu hết các newbie. Vậy làm sao để sử dụng đúng giá trị của this bên trong hàm callback?

Cơ bản về this bên trong function JavaScript

Từ khoá this bên trong function JavaScript là một từ khoá đặc biệt. Giá trị của this xác định dựa trên cách mà function được gọi, chứ không phải dựa vào vị trí mà nó được định nghĩa.

Ví dụ:

function foo() {
  console.log(this);
}

foo(); // 'this' tương ứng với đối tượng Window

const obj = {bar: foo};
obj.bar(); // 'this' tương ứng với đối tượng 'obj'

const a = new foo(); // 'this' tương ứng với đối tượng mới tạo ra 'a'

Hoặc một ví dụ khác:

const obj = {
  number: 0,
  increase: function() {
    this.number++;
  }
}

console.log(obj.number); // => 0
obj.increase();
console.log(obj.number); // => 1

Trong ví dụ trên, increase là một function. Vì vậy, giá trị của this được xác định dựa trên cách mà function này được gọi. Mà increase được gọi bằng cách nào?

Chính là obj.increase(). Suy ra, this ở đây sẽ ứng với obj. Do đó, kết quả thu được là đúng như mong đợi.

Một số cách sử dụng đúng giá trị của this bên trong hàm callback

Lưu lại reference của this

Đây là cách mà mình thường dùng trước đây. Ý tưởng của phương pháp này là mình sẽ dùng một biến bất kỳ để lưu lại giá trị của this. Khi đó, mình chả cần quan tâm this bị bind với thằng nào.

Khi đó, đoạn code trên đầu bài viết sẽ thay đổi thành:

class Helper {
  constructor() {
    this.number = 0;
  }

  increase() {
    let self = this;

    setTimeout(function() {
      self.number++;
      console.log(self.number); // => 1
    }, 100);
  }
}

const helper = new Helper();
helper.increase();

Lúc này, giá trị của self sẽ ứng với this - tương ứng với Helper.

Sử dụng phương thức bind

Phương thức bind sẽ tạo ra một function mới mà khi function này được gọi, giá trị của this sẽ tương ứng với giá trị truyền vào.

Áp dụng bind vào đoạn code trên, mình sẽ thu được:

class Helper {
  constructor() {
    this.number = 0;
  }

  increase() {
    setTimeout((function() {
      this.number++;
      console.log(this.number); // => 1
    }).bind(this), 100);
  }
}

const helper = new Helper();
helper.increase();

Kết quả thu được vẫn chính xác như mong đợi.

Sử dụng arrow function

Arrow function khác với function thông thường là nó không có this. Vì vậy, từ khoá this bên trong arrow function sẽ được đối xử như các biến bình thường khác.

Bây giờ, đoạn code trên sẽ thay đổi thành:

class Helper {
  constructor() {
    this.number = 0;
  }

  increase() {
    setTimeout(() => {
      this.number++;
      console.log(this.number); // => 1
    }, 100);
  }
}

const helper = new Helper();
helper.increase();

Trực tiếp set giá trị của this

Một số phương thức cho phép trực tiếp set giá trị của biến this. Ví dụ các phương thức ứng với Array như: map(), forEach(), filter(),...

Ví dụ:

class Helper {
  constructor() {
    this.number = [0, 1];
    this.amount = 1000;
  }

  increase() {
    this.number.forEach(function(item) {
      item += this.amount; // Uncaught TypeError: Cannot read property 'amount' of undefined
      console.log(item);
    });
  }
}

const helper = new Helper();
helper.increase();

Bên trong function, this có giá trị là undefined. Vì vậy, kết quả thu được là lỗi Uncaught TypeError: Cannot read property 'amount' of undefined.

Bây giờ, mình sẽ truyền giá trị của this vào phương thức forEach() bên trên để xem kết quả thế nào?

class Helper {
  constructor() {
    this.number = [0, 1];
    this.amount = 1000;
  }

  increase() {
    this.number.forEach(function(item) {
      item += this.amount;
      console.log(item);
    }, this);
  }
}

const helper = new Helper();
helper.increase();

// 1000
// 1001

Awesome! Kết quả thu được là hoàn toàn chính xác.

Lời kết

Trên đây là một số cách để sử dụng đúng giá trị của this bên trong hàm callback mà mình đã tham khảo và biên tập lại dựa vào một câu hỏi trên Stackoverflow. Bạn có thể tham khảo thêm tại đây: How to access the correct 'this' inside a callback?.

Theo bạn, ngoài những cách làm trên thì mình còn có cách nào khác nữa không? Nếu có thì bạn vui lòng chia sẻ trong phần bình luận nhé!

Xin chào và hẹn gặp lại!


★ Nếu bạn thấy bài viết này hay thì hãy ghé thăm Blog hoặc theo dõi mình trên Facebook để nhận được thông báo khi có bài viết mới nhất nhé:

☛ Blog: Complete JavaScript
☛ Facebook Fanpage: Complete JavaScript
☛ Facebook Group: Hỏi đáp JavaScript VN

Bình luận


White
{{ comment.user.name }}
Bỏ hay Hay
{{comment.like_count}}
Male avatar
{{ comment_error }}
Hủy
   

Hiển thị thử

Chỉnh sửa

White

Lam Pham

28 bài viết.
71 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
32 13
Trước khi vào nội dung bài viết. Tôi xin đính chính giúp anh trả lời những câu hỏi chỉ là tựa đề của một bài hát tôi không có ý xưng hô là anh. Bài...
Lam Pham viết hơn 1 năm trước
32 13
White
16 4
JavaScript cung cấp nhiều cách khác nhau để convert String sang Number. Và trong bài viết này, mình sẽ tổng hợp lại một số cách mà mình đã biết. ...
Lam Pham viết 8 tháng trước
16 4
White
15 6
Có thể bạn thừa biết, JavaScript là một ngôn ngữ chạy (Link). Điều đó có nghĩa là nếu bạn thực hiện một tác vụ quá lớn trên giao diện chính thì khả...
Lam Pham viết hơn 1 năm trước
15 6
Bài viết liên quan
White
59 8
Tăng sức mạnh cho javascript với lodash Lần này mình sẽ giới thiệu 1 thư viện javascript vô cùng bá đạo có tên là "lodash]1]", có thể nói nó là LI...
Huy Hoàng Phạm viết gần 4 năm trước
59 8
White
8 6
(Ảnh) Đọc bài viết gốc (Link) 1. Mở đầu Trước khi học hiểu về this bạn nên học trước về (Link) Trong js, từ khóa this là thứ rất hay nhưng cũn...
Đào Văn Hùng viết hơn 1 năm trước
8 6
{{like_count}}

kipalog

{{ comment_count }}

bình luận

{{liked ? "Đã kipalog" : "Kipalog"}}


White
{{userFollowed ? 'Following' : 'Follow'}}
28 bài viết.
71 người follow

 Đầu mục bài viết

Vẫn còn nữa! x

Kipalog vẫn còn rất nhiều bài viết hay và chủ đề thú vị chờ bạn khám phá!