Χρονομετρητές και απώλεια περιβάλλοντος στο 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. Ωστόσο, όταν πατηθεί το κουμπί
δεν συμβαίνει τίποτα. Διορθώστε το σφάλμα
του συγγραφέα του κώδικα. Γράψτε κείμενο, στο οποίο
θα δώσετε εξήγηση στον συγγραφέα του κώδικα, γιατί προέκυψε
το σφάλμα του.