ไทเมอร์และการสูญเสียคอนเท็กซ์ใน JavaScript
เมื่อใช้ไทเมอร์ในอีเวนต์แฮนด์เลอร์ เรามักจะพบปัญหาการสูญเสียคอนเท็กซ์ ลองดูตัวอย่างกัน
สมมติว่าเรามีอินพุต:
<input id="elem" value="text">
สมมติว่าเมื่อคลิกที่อินพุตนี้ ฟังก์ชันนิรนาม จะทำงาน และภายในฟังก์ชันนั้นจะมีไทเมอร์ ที่ทำงานทุกวินาทีเพื่อแสดงผลบางอย่าง ในคอนโซล:
let elem = document.querySelector('#elem');
elem.addEventListener('click', function() {
setInterval(function() {
console.log('!!!'); // แสดงผลบางอย่างในคอนโซล
}, 1000);
});
ทุกอย่างทำงานได้ถูกต้อง แต่ตอนนี้สมมติว่า
เราต้องการแสดงผลค่า value
ของอินพุตของเราในคอนโซล - เราจะต้องประหลาดใจ: ในคอนโซล
จะแสดงผลเป็น undefined:
elem.addEventListener('click', function() {
setInterval(function() {
console.log(this.value); // จะแสดงผลเป็น undefined
}, 1000);
});
ทั้งหมดนี้เป็นเพราะเรามีฟังก์ชัน
ภายในฟังก์ชัน: มีฟังก์ชันนิรนามภายนอก
ที่ถูกเรียกเมื่อคลิก และฟังก์ชันนิรนามภายใน
ที่ถูกเรียกโดยไทเมอร์
ในฟังก์ชันภายนอก this ชี้ไปที่
อินพุต แต่ในฟังก์ชันภายใน - ไม่ นี่คือกรณีของ
การสูญเสียคอนเท็กซ์
ทำไมจึงแสดงผล undefined และไม่เกิด
ข้อผิดพลาดในคอนโซล อย่างที่เคยเป็นในบทเรียนก่อนๆ?
เพราะว่า this ภายในฟังก์ชัน
ที่ถูกเรียกผ่าน setInterval ชี้ไปที่
window
นี่หมายความว่าเราพยายามอ่านคุณสมบัติ
value จากออบเจ็กต์ window แบบนี้: window.value
แต่คุณสมบัตินั้นไม่มีอยู่ในนั้น และเราได้
undefined (ไม่ใช่ข้อผิดพลาด)
มาแก้ไขปัญหาด้วยการนำเข้า self:
elem.addEventListener('click', function() {
let self = this;
setInterval(function() {
console.log(self.value);
}, 1000);
});
สมมติว่าโค้ดนี้ถูกกำหนด:
<input type="button" id="elem" value="1">
let elem = document.querySelector('#elem');
elem.addEventListener('click', function() {
setInterval(function() {
this.value = Number(elem.value) + 1;
}, 1000);
});
ผู้เขียนโค้ดต้องการให้เมื่อกดปุ่ม
ค่าของปุ่มนี้จะเพิ่มขึ้นทุกวินาที
ทีละ 1 อย่างไรก็ตาม เมื่อกดปุ่ม
แล้วไม่มีอะไรเกิดขึ้นเลย แก้ไขข้อผิดพลาด
ของผู้เขียนโค้ด เขียนข้อความที่คุณ
จะให้คำอธิบายแก่ผู้เขียนโค้ดว่าทำไมจึงเกิด
ข้อผิดพลาดของเขาขึ้น