Adloun

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 var est 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 this selon l'appel, et la fixer avec bind ou une fonction fléchée.

4.1 La portée des variables

Définition 4.1Portée

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

Proposition 4.2Portée lexicale

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.

Définition 4.3Remontée

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 cours

En accès libre, sans inscription.

Exercices corrigés de ce chapitre

Lire la suite du cours →