Minuteurs et perte de contexte en JavaScript
Lors de l'utilisation de minuteries dans les gestionnaires d'événements, nous sommes confrontés à des problèmes de perte de contexte. Regardons un exemple.
Supposons que nous ayons un champ de saisie :
<input id="elem" value="text">
Supposons qu'un clic sur ce champ déclenche une fonction anonyme et qu'à l'intérieur de cette fonction, une minuterie se mette en marche, affichant quelque chose dans la console chaque seconde :
let elem = document.querySelector('#elem');
elem.addEventListener('click', function() {
setInterval(function() {
console.log('!!!'); // nous affichons quelque chose dans la console
}, 1000);
});
Jusqu'ici, tout fonctionne correctement. Mais supposons maintenant
que nous voulions afficher la value
de notre champ dans la console - une surprise nous attend : la console
affichera undefined :
elem.addEventListener('click', function() {
setInterval(function() {
console.log(this.value); // affichera undefined
}, 1000);
});
Le problème est que nous nous retrouvons avec une fonction
dans une fonction : il y a une fonction anonyme externe,
qui est appelée lors du clic, et une fonction anonyme interne,
qui est lancée par la minuterie.
Dans la fonction externe, this fait référence
au champ de saisie, mais pas dans la fonction interne. C'est une
perte de contexte.
Pourquoi undefined est-il affiché, et non une erreur
dans la console, comme cela s'est produit dans les leçons
précédentes ? Parce que this à l'intérieur de la fonction
appelée via setInterval fait référence
à window.
Cela signifie que nous essayons de lire la propriété
value de l'objet window, comme ceci : window.value,
or une telle propriété n'existe pas, et nous obtenons
undefined (pas une erreur).
Corrigeons le problème en introduisant self :
elem.addEventListener('click', function() {
let self = this;
setInterval(function() {
console.log(self.value);
}, 1000);
});
Supposons le code suivant :
<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);
});
L'auteur du code souhaitait qu'en cliquant sur le bouton,
la valeur de ce bouton augmente de 1 chaque seconde.
Cependant, rien ne se passe lors du clic sur le bouton.
Corrigez l'erreur de l'auteur du code. Écrivez un texte dans lequel vous
expliquerez à l'auteur du code pourquoi son erreur s'est produite.