Portée, fermetures et this
Cours · JavaScript : rendre le Web interactif (école d'ingénieur), chapitre 4 · école d'ingénieur, informatique · le début du chapitre, la suite sur Adloun
Le panier de la Librairie du Campus doit se souvenir des livres choisis d'un clic à l'autre, sans que le reste du programme puisse le modifier par mégarde. Un numéro de commande doit augmenter à chaque commande, et un code promotionnel ne servir que deux fois. En Java, on écrirait une classe avec des attributs privés. JavaScript a aussi des classes (chapitre 6), mais il a d'abord un mécanisme plus simple et plus répandu : la fermeture, une fonction qui se souvient des variables de l'endroit où elle a été créée.
Pour la comprendre, il faut savoir précisément où une variable est visible, sa portée, et à quel moment elle existe. Ce chapitre l'explique, avec les pièges de l'ancien mot var, puis présente les fermetures et les fonctions d'ordre supérieur. Il se termine par this, le mot qui surprend le plus les programmeurs Java, parce qu'en JavaScript sa valeur dépend de la façon dont on appelle une fonction.
Objectifs du chapitre
- Déterminer la portée d'une variable (module, fonction, bloc) et prévoir le masquage.
- Expliquer la remontée et la zone morte temporelle ; savoir pourquoi
varest abandonné. - Écrire des fermetures : compteur, fabrique de fonctions, données privées.
- Écrire et utiliser des fonctions d'ordre supérieur.
- Prévoir la valeur de
thisselon l'appel, et la fixer avecbindou une fonction fléchée.
4.1 La portée des variables
La portée d'une variable est la partie du programme où son nom est visible. Une variable déclarée avec const ou let est visible dans le bloc où elle est déclarée (entre les accolades qui l'entourent), à partir de sa déclaration ; hors de tout bloc, elle est visible dans tout le module. Les paramètres d'une fonction sont visibles dans le corps de la fonction.
const librairie = "La Librairie du Campus"; // portée du module
function afficherTotal(prix) {
const tva = 0.055; // portée de la fonction
if (prix > 0) {
const ttc = Math.round(prix * (1 + tva)); // portée du bloc
console.log(`${librairie} : ${ttc} centimes`);
}
console.log(typeof ttc);
}
afficherTotal(3000);
console.log(typeof tva);
La Librairie du Campus : 3165 centimes
undefined
undefined
La constante librairie, déclarée au niveau du module, est visible partout, y compris dans la fonction. tva n'est visible que dans la fonction, et ttc que dans le bloc du if : hors de ce bloc, typeof ttc vaut "undefined", comme pour un nom jamais déclaré. Utiliser le nom lui-même, hors de sa portée, est une erreur :
const prix = 3000;
if (prix > 0) {
const ttc = prix * 1.055;
console.log("dans le bloc :", ttc);
}
console.log("après le bloc :", ttc);
dans le bloc : 3165
hors-bloc.mjs:6
console.log("après le bloc :", ttc);
^
ReferenceError: ttc is not defined
Ce sont les règles de Java pour les variables locales. Une différence : en Java, une variable locale ne peut pas en masquer une autre de la même méthode ; en JavaScript, un bloc intérieur peut redéclarer un nom, qui masque alors celui de l'extérieur jusqu'à la fin du bloc.
const titre = "Catalogue";
function afficher(titre) {
console.log("paramètre :", titre);
{
const titre = "Nouveautés";
console.log("bloc :", titre);
}
console.log("paramètre encore :", titre);
}
afficher("CSS");
console.log("module :", titre);
paramètre : CSS
bloc : Nouveautés
paramètre encore : CSS
module : Catalogue
La chaîne des portées
Quand le programme utilise un nom, le moteur le cherche dans la portée courante, puis dans la portée qui l'entoure dans le texte du programme, et ainsi de suite jusqu'au module, puis aux noms globaux (console, Math…). Le premier trouvé est le bon ; si aucun ne convient, c'est une ReferenceError. Cette chaîne des portées est fixée par l'endroit où le code est écrit, et non par l'endroit d'où une fonction est appelée : on parle de portée lexicale.
const devise = "€";
function creerLigne(titre, prix) {
const separateur = " : ";
function formater() {
return titre + separateur + prix + " " + devise;
}
return formater();
}
console.log(creerLigne("HTML", 24));
HTML : 24 €
La fonction formater ne déclare aucune variable : elle trouve titre, prix et separateur dans la portée de creerLigne, qui l'entoure, et devise dans celle du module.
4.2 Remontée et zone morte temporelle
Au chapitre 3, une fonction déclarée pouvait être appelée avant la ligne où elle est écrite, pas une fonction rangée dans une constante. Voici pourquoi.
Avant d'exécuter un bloc (ou un module, ou le corps d'une fonction), le moteur enregistre toutes les déclarations qu'il contient : c'est la remontée (hoisting). Une déclaration function est enregistrée avec sa valeur, la fonction : elle est utilisable dès le début du bloc. Une variable let ou const est enregistrée sans valeur : de l'entrée dans le bloc jusqu'à sa déclaration, elle est dans la zone morte temporelle, et toute utilisation est une ReferenceError.
La suite du cours est sur Adloun
Le chapitre en entier, formules et figures comprises, avec le tuteur, les fiches, les QCM et les exercices.
Lire la suite du coursEn accès libre, sans inscription.