JavaScript : Portée & closures
Où vivent les variables (portée globale, de fonction, de bloc), le hoisting, et les closures : le mécanisme qui fait tourner useState en React.
JavaScript : Portée & closures
Deux concepts qui font peur mais qui sont au cœur du langage. Comprendre la portée et les closures, c'est comprendre pourquoi ton code se comporte comme il le fait, et surtout comment useState fonctionne en React.
# La portée (scope)
La portée, c'est la réponse à la question : « où cette variable est-elle visible ? ». Il y a trois niveaux.
- Portée globale : déclarée en dehors de toute fonction, accessible partout.
- Portée de fonction : déclarée dans une fonction, visible uniquement à l'intérieur.
- Portée de bloc :
letetconstsont limitées au bloc{}où elles sont déclarées (unif, une bouclefor).
let global = "visible partout";
function exemple() {
let locale = "visible seulement dans exemple";
if (true) {
let bloc = "visible seulement dans ce if";
}
// ici, bloc n'existe plus
}let global = "visible partout"; function exemple() { let locale = "visible seulement dans exemple"; if (true) { let bloc = "visible seulement dans ce if"; } // ici, bloc n'existe plus}Règle importante : une fonction interne peut lire les variables de la fonction qui l'entoure, mais l'inverse est impossible.
# Le hoisting (en bref)
Avant d'exécuter ton code, JavaScript « remonte » les déclarations en haut de leur portée. En pratique :
- Une fonction déclarée avec
functionest utilisable avant sa ligne de définition. letetconstsont hoistées elles aussi, mais inutilisables avant leur déclaration : y accéder trop tôt lève une erreur (la zone morte temporelle, ou TDZ).
Retiens simplement : déclare tes variables avant de les utiliser, et tu n'auras jamais de mauvaise surprise.
# Les closures
Une closure, c'est une fonction qui « se souvient » des variables de l'endroit où elle a été créée, même après que cet endroit a fini de s'exécuter. La fonction emporte son environnement avec elle, comme un sac à dos.
function creerCompteur() {
let compte = 0; // variable locale à creerCompteur
return function () {
compte = compte + 1; // la fonction interne se souvient de "compte"
return compte;
};
}
const compteur = creerCompteur();
compteur(); // 1
compteur(); // 2
compteur(); // 3function creerCompteur() { let compte = 0; // variable locale à creerCompteur return function () { compte = compte + 1; // la fonction interne se souvient de "compte" return compte; };} const compteur = creerCompteur();compteur(); // 1compteur(); // 2compteur(); // 3compte n'existe nulle part en dehors de creerCompteur, et pourtant la fonction retournée continue d'y accéder et de le mettre à jour à chaque appel. C'est exactement ça, une closure : une fonction + l'environnement dans lequel elle est née.
# Pourquoi ça compte en React
Les closures sont partout en React, souvent sans qu'on s'en aperçoive :
useStaterepose dessus : à chaque rendu, la fonction du composant « capture » les valeurs de son état à cet instant précis.- Un gestionnaire d'événement (
onClick={() => ...}) est une closure qui se souvient des props et du state du rendu où il a été créé.
C'est ce qui explique qu'une variable puisse sembler « figée » dans un useEffect ou un callback : elle a été capturée par une closure. On y reviendra avec les hooks.
# À retenir
- La portée définit où une variable est accessible : globale, de fonction, ou de bloc (
let/constdans{}). - Le hoisting remonte les déclarations ; déclare avant d'utiliser pour éviter la TDZ.
- Une closure est une fonction qui garde l'accès aux variables de son environnement de création.
- Les closures sont le mécanisme qui fait fonctionner
useStateet les gestionnaires d'événements en React.