Tác giả:TAPAS ADHIKARY
Khái niệm “this” trong JavaScript là một trong những khía cạnh khó hiểu nhất của ngôn ngữ này. Tuy nhiên lại là nhân tố quan trọng để viết code “advance”, nâng cao hơn. Trong JS, this
cho phép:
- Sử dụng lại function trong các ngữ cảnh khác nhau
- Xác định tập trung vào object nào khi gọi method.
Nhắc tới this
thì điều đầu tiên phải làm đó là biết gọi funtion nào, bởi sẽ không biết cái gì ở bên trong this
cho tới khi function được gọi. Và các trường hợp của this
có thể chia thành 5 khía cạnh binding
khác nhau.
Tuyển dụng lập trình JavaScript mới nhất
Binding là gì
Trong JavaScript thì Lexical Environment
hay nói theo nghĩa đen là môi trường từ vựng, cũng như là môi trường chứa đựng code được viết ra theo mặt vật lý (physically) . Hãy xem ví dụ bên dưới, tên biến bên trong function sayName()
là lexically
function sayName() {
let name = 'someName';
console.log('The name is, ', name);
}
Trong đó Execution Context
thì liên quan đến code hiện thời đang chạt và những thứ giúp code hoạt động. Có thể có nhiều lexical environments có sẵn nhưng chỉ có một cái đang chạy lúc này, và được Execution Context quản lý.
Mỗi Execution Context lại chứa một Environment Record
. Binding
trong JavaScript có nghĩa là ghi lại identifier (variable và tên function) trong một Environment Record cụ thể.
Lưu ý: Binding
giúp kết hợp identifier (variable và tên function) với this
cho một execution context
.
Khúc này còn hơi rắc rối nhưng những phần sau sẽ giải thích rõ hơn.
Quy tắc 1: Implicit Binding hoạt động như thế nào?
Trong implicit binding thì cần phải xem đối tượng bên trái của hàm sử dụng dấu chấm (dot operater), từ đó xác định được this
đang tham chiếu tới cái gì.
let user = {
name: 'Tapas',
address: 'freecodecamp',
getName: function() {
console.log(this.name);
}
};
user.getName();
Ví dụ này thì this
đang trỏ tới user object, bởi vì bên trái hàm dấu chấm là function getName (), ta thấy có user object, vì vậy this.name trong console sẽ hiển thị Tapas.
Một ví dụ khác
function decorateLogName(obj) {
obj.logName = function() {
console.log(this.name);
}
};
let tom = {
name: 'Tom',
age: 7
};
let jerry = {
name: 'jerry',
age: 3
};
decorateLogName(tom);
decorateLogName(jerry);
tom.logName();
jerry.logName();
Trong ví dụ này thì có 2 object, tom
và jerry
. Và chúng được decorate (nâng cao) bằng cách đính kèm method logName()
.
Khi gọi hàm tom.logName().
, object tom
nằm bên trái dấu chấm cuả function logName()
. Nên this
sẽ trỏ tới object tom
và nhận giá trị của tom (this.name
tương đương với tom). Tương tự khi gọi hàm jerry.logName()
.
>>> Xem thêm: Hướng dẫn cơ bản để làm việc với Javascript regular expression
Quy tắc 2: Explicit Binding hoạt động như thế nào?
JavaScript tạo môi trường để thực hiện code mà chúng ta viết. Trong đó nó đảm nhiệm memory creation (dành cho variables, functions, objects) trong creation phase. Cuối cùng nó chãy code trong execution phase. Lúc này môi trường mới được Execution Context
.
Có nhiều loại environment trong JavaScript application. Mỗi execution context thực hiện, chạy lệnh độc lập với nhau. Nhưng sẽ có lúc cần sử dụng thứ này trong execution context này trong cái context khác. Lúc này explicit binding sẽ pht huy công dụng.
Trong explicit binding, chúng ta có thể gọi function với object khi funtion đó nằm ngoài execution context của object đó.
Để thực hiện explicit biding thì có 3 method đó là call()
, apply()
và bind()
.
Hàm call() hoạt động thế nào
Với phương thức call()
thì context với function được gọi sẽ được chuyển tới called
dưới dạng tham số (parameter).
let getName = function() {
console.log(this.name);
}
let user = {
name: 'Tapas',
address: 'Freecodecamp'
};
getName.call(user);
Ở đây, hàm call()
sẽ được gọi trên function getName()
. Function getName()
nhận giá trị this.name
. Nhưng this
trong đây là gì? Lúc này sẽ được quyết định bởi giá trị đã được chuyển đến hàm call()
.
Với trường hợp này, this
sẽ được bind đến user object bởi vì đã chuyển user như một tham số đến hàm call()
. Vì vậy this.name
sẽ nhận giá trị của thuộc tính name của user object, Tapas.
Ví dụ trên mới chỉ chuyển một argument tới hàm call()
, nhưng thực tế có thể chuyển nhiều argument như sau:
let getName = function(hobby1, hobby2) {
console.log(this.name + ' likes ' + hobby1 + ' , ' + hobby2);
}
let user = {
name: 'Tapas',
address: 'Bangalore'
};
let hobbies = ['Swimming', 'Blogging'];
getName.call(user, hobbies[0], hobbies[1]);
Argument đầu tiên đuợc chuyển tới call()
là object context với function đã được gọi. Những thuộc tính khác chỉ có thể là giá trị được sử dụng. Ở đây mình đang chuyển Swimming và Blogging là 2 thuộc tính tới function getName()
.
Tuy nhiên sẽ có trường hợp cần pass từng argument một trong call()
thì sao? Lúc này sẽ tới lượt của apply()
.
Hàm apply() hoạt động thế nào
Cách mà apply()
thực thi lệnh cũng tương đồng với call()
nhưng cho phép pass argument thuận tiện hơn.
let getName = function(hobby1, hobby2) {
console.log(this.name + ' likes ' + hobby1 + ' , ' + hobby2);
}
let user = {
name: 'Tapas',
address: 'Bangalore'
};
let hobbies = ['Swimming', 'Blogging'];
getName.apply(user, hobbies);
Lúc này ta có thể chuyển một mảng (array) chứa argument luôn, tiện hơn nhiều so với việc chuyển từng cái.
Tips: nếu bạn cần chuyển một giá trị argument hay argument mà không có giá trị thì hãy dùng call()
. Còn nếu chuyển nhiều giá trị argument thì dùng apply()
.
Hàm bind() hoạt động thế nào
Method bind()
cũng tương tự như call()
nhưng có 1 điểm khác biệt nhỏ. Thay vì gọi function trực tiếp thì bind()
trả về một hàm mới.
let getName = function(hobby1, hobby2) {
console.log(this.name + ' likes ' + hobby1 + ' , ' + hobby2);
}
let user = {
name: 'Tapas',
address: 'Bangalore'
};
let hobbies = ['Swimming', 'Blogging'];
let newFn = getName.bind(user, hobbies[0], hobbies[1]);
newFn();
Ở đây getName.bind()
không gọi function getName()
trực tiếp, nó trả về function mới, newFn
và chúng ta đang gọi hàm dưới dạng newFn()
.
Quy tắc 3: New Binding
Từ khóa new
được dùng để tạo một object mới từ constructor function.
let Cartoon = function(name, animal) {
this.name = name;
this.animal = animal;
this.log = function() {
console.log(this.name + ' is a ' + this.animal);
}
};
Bạn có thể tạo nhiều object với từ khóa new
như sau:
let tom = new Cartoon('Tom', 'Cat');
let jerry = new Cartoon('Jerry', 'Mouse');
Với quy tắc new biding, khi một function được gọi với từ khóa new, thì this bên trong function sẽ tham chiếu tới cái object mới được lập.
let tom = new Cartoon('Tom', 'Cat');
Đây là function Cartoon được gọi với từ khóa new. Thì this sẽ tham chiếu tới object mới, tom.
>>> Xem thêm: JavaScript là gì? Làm thế nào để trở thành lập trình viên JavaScript?
Quy tắc 4: Global Object Binding
Đoạn code dưới đây có kết quả thế nào? this
tham chiếu tới cái gì?
let sayName = function(name) {
console.log(this.name);
};
window.name = 'Tapas';
sayName();
Nếu this
không giải quyết được với một trong những quy tắc binding, implicit
, explicit
hay new
, thì this
lúc này tham chiếu tới object window(global)
.
Tuy nhiên quy tắc strict mode của JavaScript sẽ không cho phép default binding như sau:
"use strict";
function myFunction() {
return this;
}
Trường hợp này thì this
là undefined
.
Quy tắc 5: HTML Event Element Biding
Trong HTML event handlers, this
tham chiếu tới element HTML nào nhận event đó.
<button onclick="console.log(this)">Click Me!</button>
Đây là giá trị trả về console khi click vào button:
"<button onclick='console.log(this)'>Click Me!</button>"
Bạn có thể thay đổi style của this
:
<button onclick="this.style.color='teal'">Click Me!</button>
Nhưng hãy cẩn thận khi call function trên button click và dùng this
bên trong function đó.
<button onclick="changeColor()">Click Me!</button>
và JavaScript:
function changeColor() {
this.style.color='teal';
}
Đoạn code trên sẽ không trả kết quả như ý được, bởi theo như rule số 4 thì this
sẽ tham chiếu tới object global, và không có object style để set màu.
Tổng kết
- Với implicit binding thì
this
trỏ tới object bên trái của hàm dấu chấm. - Với explicit binding, chúng ta có thể gọi function với object khi function đó nằm ngoài execution context của object, có thể dùng các hàm như
call()
,apply()
,bind()
. - Khi một function được gọi với từ khóa
new
thìthis
bên trong dunction sẽ trỏ tới object mới được lập. - Khi
this
không được giải quyết vớiimplicit
,explicit
haynew
thìthis
trỏ tới objectwindow(global)
. Với strict mode của JavaScript,this
trở nên undefined. - Trong HTML event handler thì
this
trỏ tới HTML element nào nhận event đó.
Tham khảo bài viết gốc tại freeCodeCamp
Có thể bạn quan tâm:
- Coding Standard JavaScript giúp code luôn gọn gàng, dễ đọc
- Vượt qua các bài phỏng vấn Javascript
- 12 tips hay cho JavaScript
Xem thêm việc làm Front-End Developer hấp dẫn tại TopDev