Tìm hiểu **this** context trong Javascript
Javascript
314
White

duthaho viết ngày 24/05/2019

Từ khóa this tương đối đơn giản trong các ngôn ngữ khác như C++, C# nhưng trong Javascript sida thì không. Để trở thành master Javascript, bắt buộc bạn phải hiểu contextthis, nhưng đừng lo lắng, chúng ta cùng tìm hiểu rõ về các khái niệm ngay bây giờ nhé!

this được xác định bởi nơi function được gọi.

Để hiểu this, chúng ta cần xác định xem hàm được gọi ở đâu, hay nói cách khác, ngữ cảnh (context) khi gọi hàm là gì.

Đầu tiên chúng ta lần lượt tìm hiểu về các trường hợp của this nhé!

Implicit Binding

Xảy ra trong trường hợp chúng ta gọi hàm sử dụng dấu chấm(dot notation)

Ví dụ:

let MyObject = function (){
    this.name = 'MyObjectName';
    this.myProperty = 'property';
};

MyObject.prototype.doStuff = function (action) {
    console.log(this.name + ' is ' + action + '!');
}

let obj = new MyObject();

obj.doStuff('awesome'); // 'MyObjectName is awesome!'

Trong ví dụ trên, thì đối tượng bên trái của dấu chấm (obj) sẽ trở thành context của this trong hàm doStuff. Tất cả Implicit Binding tuân theo quy tắc này ngoại trừ trường hợp ở cuối bài viết.

Explicit Binding

Xảy ra trong trường hợp chúng ta dùng .call(), .apply(), or .bind() cho các hàm chứa this.

Sở dĩ chúng ta gọi là explicit (tường minh) vì chúng ta sẽ xác định một cách rõ ràng ngữ cảnh của this bằng cách truyền vào call(), apply() hay bind() các tham số mình muốn.

Sử dụng như thế nào???

.call() & .apply()

Với call(), chúng ta truyền this context vào tham số thứ nhất, các tham số tiếp theo sẽ được truyền vào như những tham số của hàm:

myFunc.call(thisContext, param1, param2, ... );

Xem ví dụ sau, sử dụng lại MyObject bên trên:

let runner = { name: 'duthaho', myFavoriteActivity: 'running' };
MyObject.prototype.doStuff.call(runner, runner.myFavoriteActivity); // 'duthaho is running!';

Trong ví dụ trên, ngữ cảnh của this sẽ là runner (tham số thứ nhất truyền vào hàm call()).

Tương tự như call(), chúng ta truyền this context vào hàm với cú pháp như sau, khác với call() là truyền vào mảng các tham số của hàm:

myFunc.apply(thisContext, [param1, param2, ...]);

Ví dụ:

let runner = { name: 'duthaho', myFavoriteActivity: 'running' };
MyObject.prototype.doStuff.apply(runner, [runner.myFavoriteActivity]); // 'duthaho is running!';

Default Binding

Khi chúng ta gọi hàm mà ko dùng các quy tắc trên, this context sẽ là global context. Hay nói cách khác, this sẽ là global object (đối tượng global).

Nếu bạn đang ở trình duyệt, giá trị của this sẽ làwindow, khi đang trong strict mode, giá trị sẽ là undefined.

Thử function sau trong Chrome:

function printMe = function () {
    console.log(this);
}
printMe() // in ra đối tượng window!

Tóm lại

Hãy nhớ các quy tắc sau

  1. Gọi hàm với dấu chấm? Đối tượng được gọi chính là this.
  2. Gọi hàm với .call() và .apply()? Tham số thứ nhất chính là this.
  3. Gọi hàm ngoài 2 quy tắc trên? Cần xác định global context là gì? Đó chính là this.

this được xác định bởi nơi mà nó được gọi (tiếng Anh gọi là callsite).

.bind() – Trường hợp ngoại lệ

Với bind() thì mọi chuyện phức tạp hơn một chút. Khi gọi một hàm với bind(), chúng ta sẽ xác định được this context tại thời điểm đó bằng cách truyền tham số theo cú pháp:

myFunc.bind(thisContext);

Nó sẽ trả về 1 hàm mới với this context đã được binding.

Ví dụ:

let sayMyName = function () {
  console.log('My name is ' + this.name);
};

let user = {
  name: 'duthaho'
}

var sayMyName = sayMyName.bind(user);
sayMyName(); // 'My name is duthaho'

Khi dùng hàm đã được binding, this context luôn không đổi, vì vậy chúng ta có thể gọi hàm ở bất kỳ đâu mà vẫn giữ được this context ban đầu. Khó khăn khi dùng bind() là khi muốn biết được this context, chúng ta phải tìm đến nơi hàm được binding

this trong callback

Chúng ta thường nhầm lẫn this context khi nó được dùng trong callback (chúng ta sẽ tìm hiểu callback trong bài viết sau).

Ví dụ:

let MyObject = function (){
  this.name = 'MyObjectName';
  this.myProperty = 'property';
};

MyObject.prototype.doStuff = function (action) {
  console.log(this.name + ' is ' + action + '!');
}

let obj = new MyObject();

setTimeout(obj.doStuff, 1000, 'awesome'); // prints ' is awesome!' after a 1 second delay.
                 ^ Hàm callback!

Kết quả ngoài dự đoán, nó chỉ in ra ' is awesome!', this.name là undefined.

Chẳng phải đây là Implicit Binding sao? Nó tuân theo quy tắc dot notation?

Hãy nhìn kỹ lại một chút, obj.doStuff không hề có dấu (), vì vậy ở đây chúng ta không gọi hàm doStuff, chúng ta chỉ truyền tên hàm như là một tham số vào trong hàm setTimeout mà thôi. Vì vậy đây không phải là Implicit Binding.

Vì vậy, khi hàm doStuff được gọi sau 1 giây, nó nằm trong 1 ngữ cảnh khác, global context. Ở đây là window object, và nó không có thuộc tính name.

Bạn hãy thử giải quyết vấn đề trên nhé!!!

Solution:

let MyObject = function (){
  this.name = 'MyObjectName';
  this.myProperty = 'property';
};

MyObject.prototype.doStuff = function (action) {
  console.log(this.name + ' is ' + action + '!');
}

let obj = new MyObject();

setTimeout(obj.doStuff.bind(obj), 1000, 'awesome'); // prints 'MyObjectName is awesome!' after a 1 second delay.

Hy vọng qua bài viết này, chúng ta đã một phần nào hiểu rõ hơn về this context trong Javascript. Hẹn gặp lại trong bài viết tuần sau về callback!
Ghé thăm blog của mình nhé!

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

duthaho

14 bài viết.
57 người follow
Kipalog
{{userFollowed ? 'Following' : 'Follow'}}
Cùng một tác giả
White
54 6
Chào mừng các bạn trở lại với blog của (Link). Dù Tết nhất dí tới this rồi nhưng vẫn cố gắng làm một bài viết tất niên. Thật ra mình dell biết chữ ...
duthaho viết 8 tháng trước
54 6
White
32 5
Chào mừng bạn trở lại với blog của (Link). Ông bà ta thường nói _học phải đi đôi với hành_, qua một số bài viết về lý thuyết, từ cơ bản đến nâng ca...
duthaho viết 10 tháng trước
32 5
White
21 6
Thế hệ 9x chắc ai cũng biết bài hit đình đám của Ưng Hoàng Phúc Hứa thật nhiều, thất hứa thì cũng thật nhiều? Không biết có phải vì quá nổi tiếng k...
duthaho viết hơn 1 năm trước
21 6
Bài viết liên quan
White
70 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 5 năm trước
70 8
White
10 1
_Có mấy chia sẻ nhỏ, mình muốn đưa ra để mọi người cùng thảo luận góp ý. Thread này không tập trung vào Technical nữa mà discuss về Coding Style & ...
Hùng Phong viết gần 2 năm trước
10 1
White
38 8
Lâu không post gì muốn viết một bài dài dài về js cơ mà đau đầu quá viết mãi không xong, thôi post bài ngắn vậy :smiley: Lấy screen size ở đây tôi...
Hà Phạm viết gần 5 năm trước
38 8
{{like_count}}

kipalog

{{ comment_count }}

bình luận

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


White
{{userFollowed ? 'Following' : 'Follow'}}
14 bài viết.
57 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á!