-----
1.1.1
Từ khóa this
Trong JavaScript, this
là một từ khóa (keyword), được sử dụng để trỏ tới một đối tượng (object). Tuy
nhiên, tùy theo tình huống sử dụng, từ khóa this sẽ trỏ tới đối tượng khác nhau.
Ví dụ:
– Trong một phương thức của đối tượng, từ khóa this sẽ tham
chiếu tới chính đối tượng chứa nó
– Ở vị trí tự do, từ khóa this sẽ tham chiếu tới đối tượng
toàn cục (global object)
– Trong một hàm (hàm tự do, không thuộc đối tượng nào), từ
khóa this sẽ tham chiếu tới đối tượng toàn cục
– Trong một hàm, ở chế độ strict mode, từ khóa this sẽ là
undefined
– Trong một sự kiện (event), từ khóa this tham chiếu tới phần
tử (element) tiếp nhận sự kiện
– Có thể sử dụng các phương thức bind(), apply() và call() để
thay đổi đối tượng tham chiếu của từ khóa this
Để hiểu rõ hơn các tình huống sử dụng của từ khóa this, chúng
ta sẽ cùng xem xét một số ví dụ.
Ví dụ 1, trong một phương thức của đối tượng, từ khóa this sẽ
tham chiếu tới chính đối tượng chứa nó.
// định nghĩa đối tượng conNguoi
const conNguoi = {
ho : 'Nguyen',
ten : 'Teo',
hoVaTen : function() {
return(this.ho + ' ' + this.ten);
},
test : function(){
console.log(this);
}
};
// xuất thử xem conNguoi có phải là đối tượng không?
console.log(typeof conNguoi); //object
// xuất thử xem this trong object conNguoi tham chiếu tới đối tượng nào
console.log(conNguoi.test()); //conNguoi
Ví dụ 2, trong một phương thức của đối tượng, từ khóa this sẽ
tham chiếu tới chính đối tượng chứa nó, trường hợp đối tượng lồng đối tượng.
// định nghĩa đối tượng conNguoi
const conNguoi = {
ho : 'Nguyen',
ten : 'Teo',
test : function(){
console.log(this);
},
sinhVien : {
diem : 8,
testSinhVien : function() {
console.log(this);
}
}
};
// xuất từ khóa this trong đối tượng sinhVien
console.log(conNguoi.sinhVien.testSinhVien());
// sinhVien
// xuất từ khóa this trong đối tượng
conNguoi
console.log(conNguoi.test()); // conNguoi
Bạn để ý, từ khóa this sẽ thuộc về đối tượng gần nhất gọi tới
nó. Ví dụ, trong dòng mã conNguoi.sinhVien.testSinhVien()thì đối tượng sinhVien gần
hơn so với đối tượng conNguoi.
Ví dụ 3, ở vị trí tự do, từ khóa this sẽ tham chiếu tới đối
tượng toàn cục (global object).
<script>
console.log(this); // Window
</script>
Ví dụ 4, trong một hàm (tự do, không thuộc đối tượng nào), từ
khóa this sẽ tham chiếu tới đối tượng toàn cục.
function testFunction() {
console.log(this);
}
testFunction(); // Window
Ví dụ 5, trong một hàm, ở chế độ strict mode, từ khóa this sẽ
là undefined
'use strict'
function testFunction() {
console.log(this);
}
testFunction(); // undefined
Ví dụ 6, trong một sự kiện (event), từ khóa this tham chiếu
tới phần tử (element) tiếp nhận sự kiện.
<body>
<button id="btn">Dang nhap</button>
<script>
const btn = document.getElementById("btn");
btn.onclick = function() {
this.style.display = "none"; // làm biết mất nút Dang nhap
console.log(this); // element button
console.dir(this); // button object
}
</script>
1.1.2 Phương thức bind()
Phương thức
bind() là phương thức có sẵn của đối tượng function, nó được gọi là function
prototype, do vậy chỉ có function mới gọi được phương thức này. Bạn có thể sử dụng
lệnh console.dir(Function) để kiểm
tra.
Bạn có thể
sử dụng phương thức bind() để:
– Mượn phương
thức của đối tượng khác
– Xác định
tham số this của một phương thức
Mượn
phương thức của đối tượng khác
Phương thức bind()
cho phép đối tượng này có thể mượn phương thức (hay mượn hàm – function
borrowing) của đối tượng khác. Ví dụ, thông thường để định nghĩa hai đối tượng
là conNguoi va sinhVien bạn sẽ làm như sau:
// định nghĩa đối tượng conNguoi
const conNguoi = {
ho : 'Nguyen',
ten : 'Teo',
hoVaTen : function() {
return(this.ho + ' ' + this.ten);
}
};
// định nghĩa đối tượng sinhVien
const sinhVien = {
ho : 'Le',
ten : 'Ti',
hoVaTen : function() {
return(this.ho + ' ' + this.ten);
}
};
console.log(conNguoi.hoVaTen());
console.log(sinhVien.hoVaTen());
Với phương thức bind(),
khi định nghĩa đối tượng sinhVien, bạn
sẽ không phải viết lại phương thức hoVaTen,
mà khi cần sử dụng, bạn sẽ mượn từ đối tượng conNguoi.
Lưu ý: phương thức bind() sẽ trả về một hàm mới, để bạn sử dụng
về sau, chứ nó không gọi hàm để thực thi trực tiếp.
// định nghĩa đối tượng conNguoi
const conNguoi = {
ho : 'Nguyen',
ten : 'Teo',
hoVaTen : function() {
return(this.ho + ' ' + this.ten);
}
};
// định nghĩa đối tượng sinhVien
const sinhVien = {
ho : 'Le',
ten : 'Ti'
};
let hoVaTenSinhVien = conNguoi.hoVaTen.bind(sinhVien);
console.log(hoVaTenSinhVien());
// hoặc
console.log(conNguoi.hoVaTen.bind(sinhVien)());
Ở đoạn mã let hoVaTenSinhVien = conNguoi.hoVaTen.bind(sinhVien); đối số sinhVien truyền vào cho phương thức bind() sẽ đóng vai trò là từ khóa this trong phương thức hoVaTen() của đối tượng conNguoi.
Xác định tham số this của phương thức
Từ khóa this được
sử dụng để xác định đối tượng được tham chiếu tới trong các tình huống sử dụng
cụ thể, hay nói cách khác là trong các tình huống khác nhau thì từ khóa this sẽ trỏ tới đối tượng khác nhau. Tình
huống sử dụng ở đây được gọi là ngữ cảnh sử dụng (context). Khi lập trình, nếu
không kiểm soát được từ khóa this đang
trỏ tới đối tượng nào thì sẽ làm cho chương trình thực thi ngoài ý muốn, do vậy
cần phải có cách nào đó để kiểm soát được từ khóa this, sử dụng phương thức bind()
là một lựa chọn. Bạn cùng quan sát ví dụ sau.
Đoạn chương trình sau sẽ hiển thị họ tên sinh viên ra màn
hình:
<body>
<p id="ket-qua"></p>
<script>
// định nghĩa đối tượng sinhVien
sinhVien = {
ho : 'Nguyen Van',
ten : 'Teo',
xuatHoTen : function() {
let kq = document.getElementById("ket-qua");
kq.innerHTML = this.ho + ' ' + this.ten;
}
};
sinhVien.xuatHoTen();
</script>
</body>
Chạy đoạn mã trên sẽ có kết quả như mong muốn. Tuy nhiên, khi
chúng ta sử dụng phương thức xuatHoTen()
như là một hàm callback (xuatHoTen()
là tham số truyền vào, và được thực thi trong một hàm khác – hàm setTimeout)
thì đoạn mã sẽ cho ra kết quả không như mong muốn như ví dụ sau,
sinhVien = {
ho : 'Nguyen Van',
ten : 'Teo',
xuatHoTen : function() {
let kq = document.getElementById("ket-qua");
kq.innerHTML = this.ho + ' ' + this.ten;
}
};
setTimeout(sinhVien.xuatHoTen, 3000);
Đoạn mã trên thay vì xuất Nguyen Van Teo thì lại xuất undefined
ra màn hình. Nghĩa là từ khóa this đã không trỏ tới đối tượng sinhVien nữa, nên không lấy được họ tên
sinh viên. Giải pháp là sẽ sử dụng phương thức bind() để kết buộc sinhVien.xuatHoTen() với đối tượng sinhVien, khi
đó từ khóa this trong lệnh kq.innerHTML = this.ho + ' ' + this.ten; sẽ trỏ tới đối tượng sinhVien lúc thực thi.
sinhVien = {
ho : 'Nguyen Van',
ten : 'Teo',
xuatHoTen : function() {
let kq = document.getElementById("ket-qua");
kq.innerHTML = this.ho + ' ' + this.ten;
}
};
setTimeout(sinhVien.xuatHoTen.bind(sinhVien), 3000);
1.1.3 Từ khóa this và arrow function
Như bạn đã biết, hiện tượng bị mất ngữ cảnh của từ khóa this khi dùng nó trong hàm callback, như
trong ví dụ dưới đây, mục đích là sẽ xuất danh sách các ngọn núi tại Đà Lạt sau
một giây:
const dalat = {
mountains: ["langbiang", "voi", "bidoup", "pinhat"],
print: function(delay = 1000) {
setTimeout(function() {
console.log(this.mountains.join(', '));
}, delay);
}
};
dalat.print();// Uncaught TypeError: Cannot read property 'join' of undefined
Tuy nhiên, chạy đoạn mã trên sẽ có lỗi, lý do là từ khóa this trỏ tới đối tượng Window, mà đối tượng Window không có thuộc tính mountains. Có thể khắc phục lỗi này bằng
cách dùng phương thức bind() như sau:
const dalat = {
mountains: ["langbiang", "voi", "bidoup", "pinhat"],
print: function(delay = 1000) {
console.log(this.mountains); // ['langbiang', 'voi', 'bidoup',
'pinhat']
setTimeout(function(){
console.log(this.mountains); // undefined
console.log(this.mountains.join(', '));
}, delay);
}
};
dalat.print();
Ở đoạn mã trên bạn thử xuất console.log(this.mountains); //
['langbiang', 'voi', 'bidoup', 'pinhat'] sẽ thấy từ khóa this vẫn tham chiếu tới mảng mountains được. Tuy nhiên, xuất this.mountains trong hàm setTimeout sẽ là undefined, do this bị mất ngữ cảnh.
Để lấy lại ngữ cảnh
cho từ khóa this, bạn sẽ kết buộc hàm
callback của setTimeout với đối tượng
dalat. Để ý: kết buộc hàm callback của
setTimeout chứ không phải kết buộc setTimeout với đối tượng dalat.
const dalat = {
mountains: ["langbiang", "voi", "bidoup", "pinhat"],
print: function(delay = 1000) {
console.log(this.mountains); // ['langbiang', 'voi', 'bidoup',
'pinhat']
setTimeout(function(){
console.log(this.mountains); // ['langbiang', 'voi', 'bidoup',
'pinhat']
console.log(this.mountains.join(', '));
}.bind(dalat), delay);
}
};
dalat.print(); // langbiang,
voi, bidoup, pinhat
Nếu không dùng phương thức bind() như ở trên, bạn có thể sử
dụng cách đơn giản hơn để sửa lỗi kết buộc là chuyển hàm callback của
setTimeout thành arrow function,
const dalat = {
mountains: ["langbiang", "voi", "bidoup", "pinhat"],
print: function(delay = 1000) {
setTimeout(() => {
console.log(this.mountains.join(', '));
}, delay);
}
};
dalat.print();// langbiang, voi, bidoup, pinhat
Tuy nhiên, nếu chuyển tiếp hàm print thành arrow function, thì từ khóa this lại trỏ tới đối tượng Window.
const dalat = {
mountains: ["langbiang", "voi", "bidoup", "pinhat"],
print: (delay = 1000) => {
setTimeout(() => {
console.log(this.mountains.join(', '));
}, delay);
}
};
dalat.print();// Uncaught TypeError: Cannot read
property 'join' of undefined
1.1.4 Xem và đọc thêm
– Dùng các từ khóa sau tìm kiếm trên mạng để đọc thêm: từ khóa
this trong JavaScript, bind() method.
– This (w3schools.com): https://www.w3schools.com/js/js_this.asp
– This (mozilla): https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this?retiredLocale=vi
– bind() method: https://www.w3schools.com/js/js_function_bind.asp
– Phương thức bind() – F8: https://www.youtube.com/watch?v=F5z6YoR8of0
https://www.youtube.com/watch?v=6j9b2_E34JM
– [1] Alex Banks, Eve Porcello, Learning React – Mordern Patterns
for Developing React Apps, O’Reilly Media, 2020, p12 – p17
1.1.5 Bài tập và thực hành
Bài tập 1. Viết lại các đoạn mã trong phần lý thuyết để chạy
và kiểm tra lại kết quả.
1.1.6 Câu hỏi ôn tập
Câu 1. Quan sát đoạn mã JavaScript sau, lệnh console.log(this) sẽ xuất gì ra cửa sổ
console?
<script>
let x = this;
console.log(this)
</script>
A. Thông báo lỗi
B. this
C. x
D. window
Câu 2. Đoạn mã JavaScript sau xuất gì ra cửa sổ console?
function
testFunction() {
console.log(this);
}
testFunction();
A. window
B. testFunction
C. this
D. function
Câu 3. Trong JavaScript, bạn có thể sử dụng phương thức bind()
để làm gì?
A. Mượn phương thức của đối tượng khác
B. Xác định tham số this
của một phương thức
C. Xác định phạm vi của biến
D. Đáp án A và B
Câu 4. Trong JavaScript, phương thức setTimeout(function,
milliseconds) dùng để làm gì?
A. Thực thi một đoạn mã sau một khoảng thời gian định trước
B. Là một phương thức của đối tượng window
C. Thiết lập thời gian cho máy tính
D. Đáp án A và B
Câu 5. The ______ method creates a new function that, when
called, has its this keyword set to the provided value, with a given sequence
of arguments preceding any provided when the new function is called.
A. let
B. bind()
C. new
D. const
-----
Đáp án: 1 (D), 2 (A), 3 (D), 4 (D), 5 (B)
Bài tiếp: Web nâng cao (5) - JavaScript cho React (4)