Portée des variables en JavaScript
Comprenez la portée des variables en JavaScript : portée globale, de fonction et de bloc, différences entre var, let et const, hoisting et TDZ.
La portée est la partie d'un programme où une variable est visible et peut être utilisée. Comprendre la portée vous permet de prédire à quelle valeur un nom fait référence, d'éviter les conflits accidentels entre variables, et de comprendre pourquoi une variable est ou n'est pas disponible à un endroit donné. Ce guide explique les types de portée en JavaScript, comment les fonctions imbriquées accèdent aux portées qui les entourent, en quoi var diffère de let/const, et comment tout cela mène naturellement aux closures.
Les trois types de portée
JavaScript détermine la visibilité des variables en fonction de l'endroit où la variable est déclarée dans le code source — c'est ce qu'on appelle la portée lexicale (ou statique). Il existe trois niveaux :
- Portée globale — déclarée en dehors de toute fonction ou bloc. Visible partout.
- Portée de fonction — déclarée à l'intérieur d'une fonction. Visible uniquement dans cette fonction (c'est ainsi que fonctionne
var). - Portée de bloc — déclarée avec
letouconstà l'intérieur d'un bloc{ ... }(unif, unfor, ou même un simple{}). Visible uniquement dans ce bloc.
Portée globale
Une variable déclarée au niveau supérieur d'un script est globale : toute fonction et tout bloc peut la lire et la modifier.
Les variables globales sont pratiques mais risquées : n'importe quelle partie du programme peut les modifier, et deux portions de code indépendantes peuvent choisir le même nom et s'écraser mutuellement. Gardez l'espace de noms global au minimum.
Portée de fonction
Une variable déclarée à l'intérieur d'une fonction n'existe que pendant l'exécution de cette fonction et est invisible de l'extérieur.
Le try...catch permet à l'exemple de continuer à s'exécuter afin que vous puissiez voir le message d'erreur au lieu que le script s'arrête simplement.
Portée de bloc
let et const sont limités au bloc englobant le plus proche, pas à la fonction entière. Une variable déclarée à l'intérieur d'un bloc if ou for disparaît dès que le bloc se termine.
Comment les fonctions imbriquées voient les variables externes
Lorsque vous imbriquez des fonctions, la fonction interne peut lire les variables de toutes les portées qui l'entourent — la sienne, celle de la fonction englobante, et la portée globale. Cette chaîne de portées accessibles s'appelle l'environnement lexical (ou chaîne de portées).
Lorsque JavaScript recherche un nom, il commence dans la portée courante et remonte vers l'extérieur jusqu'à trouver la variable. S'il atteint la portée globale sans la trouver, vous obtenez une ReferenceError.
La recherche ne se fait que vers l'extérieur, jamais vers l'intérieur — outer() ne peut pas voir innerVar. C'est exactement le mécanisme qui fait fonctionner les closures : une fonction interne conserve l'accès à ses variables externes même après que la fonction externe a retourné.
var vs let et const
Les règles de portée diffèrent selon la façon dont vous déclarez une variable. var est limité à la fonction et ignore les blocs, tandis que let et const sont limités au bloc. C'est l'une des sources de bogues les plus fréquentes dans l'ancien code.
Le piège classique est une boucle qui crée des callbacks. Avec var, il n'y a qu'une seule variable partagée ; avec let, chaque itération possède son propre lien :
Pour une étude approfondie des particularités de var — notamment le hoisting et l'absence de portée de bloc — consultez l'ancien "var".
Hoisting et la Zone Morte Temporelle
Les déclarations sont traitées avant l'exécution du code. Une déclaration var est hoistée et initialisée à undefined, donc la lire avant l'assignation donne undefined plutôt qu'une erreur. let et const sont également hoistés, mais ils restent dans une Zone Morte Temporelle (TDZ) — inutilisables jusqu'à l'exécution de la ligne qui les déclare.
Bonnes pratiques
- Préférez
const, puislet, évitezvar. La portée de bloc est plus prévisible et évite les fuites accidentelles ainsi que le piège de la boucle avec callbacks évoqué ci-dessus. - Gardez les variables dans la portée la plus restreinte possible. Déclarez-les à l'intérieur du bloc ou de la fonction qui en a besoin, plutôt qu'au niveau supérieur.
- Limitez les variables globales. Moins il y en a, moins il y a de conflits de noms et de valeurs que n'importe quel code peut modifier silencieusement.
- Déclarez avant d'utiliser. Même si le hoisting existe, s'y fier rend le code plus difficile à lire.
Conclusion
La portée contrôle où chaque variable est visible : globale, de fonction ou de bloc. Les fonctions imbriquées forment un environnement lexical qui permet au code interne d'accéder aux portées extérieures via la chaîne de portées — le fondement des closures. Choisir let/const plutôt que var vous offre une portée de bloc fiable et évite les surprises liées au hoisting. Ensuite, étudiez comment les fonctions capturent leur environnement dans les Closures JavaScript et comment les fonctions retournées se comportent dans les expressions de fonction.