Var, let et const en JavaScript : le trio infernal expliqué (avec une touche de TypeScript)
Salut, toi qui t'arraches les cheveux avec ces fameuses variables en JavaScript ! Tu te demandes quand utiliser var, let ou const sans te ridiculiser en réunion d'équipe ? Ne bouge pas, on va démêler tout ça ensemble, tranquillement et avec le sourire. Au programme : on explore chaque mot-clé, on compare leur portée (globale, fonction, bloc), on rigole un peu du hoisting (ce mécanisme un peu magique de JavaScript), on balance de petits exemples concrets, et on termine par un soupçon de TypeScript. Prêt ? C'est parti !
Le mot-clé var : l'ancêtre imprévisible
Commençons par var, le doyen des variables JavaScript. Si JavaScript était une série, var serait le personnage un peu old-school, imprévisible mais attachant. Jusqu'à ES5, on n'avait que lui pour déclarer nos variables. Tu peux toujours l'utiliser aujourd'hui, mais attention : il a quelques particularités qui peuvent surprendre.
Caractéristiques de var
- Portée de var : Une variable déclarée avec var est limitée à la fonction qui la contient (ou globale si déclarée en dehors de toute fonction). Elle ignore les accolades de bloc (if, for, etc.). En clair, un var déclaré dans un bloc reste accessible en dehors du bloc, du moment qu'on est dans la même fonction.
- Redéclaration permise : Avec var, tu peux déclarer la même variable plusieurs fois dans la même portée sans erreur. Par exemple :
var x = 1; var x = 2;ne déclenchera pas d'erreur – JavaScript va juste écraser la première valeur par la seconde. Pratique pour le vieux code, mais source de confusion à éviter dans le nouveau code.
Pour illustrer la portée un peu trop large de var, voici un petit exemple :
javascript
Eh oui, même si ce fruit était déclaré à l'intérieur d'un if, le console.log en dehors du bloc l'affiche sans problème. var se moque des frontières de bloc : il est soit global, soit attaché à sa fonction englobante, un point c'est tout.
Et le meilleur (ou le pire) pour la fin : le hoisting. En français on parle de hissage, mais soyons honnêtes, on utilise surtout le terme hoisting. C'est la tendance qu'a var à se déclarer en douce en haut de la portée. JavaScript, lors de l'exécution, fait comme si toutes les déclarations var avaient été « remontées » tout en haut de la fonction. Du coup, tu peux utiliser ta variable avant même sa ligne de déclaration… avec une valeur par défaut un peu étrange. Regardons ça de plus près dans la section hoisting plus bas (spoiler : var adore te surprendre avec undefined).
En résumé, var a rendu de fiers services, mais il est un peu fourbe sur les bords : portée large, redéclaration silencieuse, hoisting imprévisible… On peut faire mieux, et ça tombe bien, ES6 a introduit de nouveaux keywords pour remettre de l'ordre !
Le mot-clé let : la nouvelle variable bien élevée
En 2015, JavaScript a accueilli let (ainsi que const). Et là, on a eu droit à un petit lifting des variables. let est un peu le cool kid discipliné : il corrige la plupart des bizarreries de var tout en restant flexible.
Caractéristiques de let
- Portée de let : Une variable déclarée avec let est limitée au bloc dans lequel elle est définie. Un bloc, c'est n'importe quoi entre { ... } – que ce soit une fonction, un if, une boucle for, etc. Si tu déclares un let à l'intérieur d'un bloc, il n'existe pas en dehors. Enfin un peu d'ordre ! Fini la variable qui s'échappe du if ou de la boucle sans permission.
- Pas de redéclaration : Contrairement à var, tu ne peux pas redéclarer deux fois la même variable let dans une même portée. Si tu t'amuses à faire
let x = 1; let x = 2;dans le même bloc, tu auras droit à une jolie erreurSyntaxError: Identifier 'x' has already been declared. Ça évite bien des confusions, avoue.
Pour bien voir la différence de portée par rapport à var, comparons avec l'exemple précédent en remplaçant var par let :
javascript
Avec let, une fois sorti des accolades du if, terminé, la variable legume n'est plus définie. Essaie de la loger et bam : ReferenceError. Ça, c'est let qui fait respecter les règles de bonne conduite en scoping.
Et le hoisting dans tout ça ? Les variables let (et const) sont également hoistées, mais différemment. Elles ne sont pas utilisables avant d'être déclarées. Techniquement, le moteur JS sait qu'elles existent dans le bloc (il réserve la mémoire), mais tant que la ligne de déclaration n'est pas exécutée, toute tentative d'accès déclenchera une erreur. On dit que ces variables sont dans la zone morte temporelle (oui, ça fait film d'horreur, mais c'est juste le nom chic pour dire "inaccessibles avant initialisation"). On détaille ce comportement un peu plus loin, mais retiens déjà que let ne te laissera pas faire le fou furieux en utilisant une variable trop tôt.
En pratique, let est devenu le choix par défaut pour les variables qui vont changer au cours du temps. Il apporte de la clarté (portée de bloc) et de la sécurité (pas de redéclaration hasardeuse, pas d'accès avant déclaration). Bref, let est ton nouveau copain pour déclarer des variables temporaires ou modifiables.
Le mot-clé const : la valeur sûre (mais pas figée)
Au tour de const ! Introduit en même temps que let, ce mot-clé crée des constantes… ou presque. Disons qu'il crée des variables dont la référence ne changera pas. const a la même portée de bloc que let (fini les var qui se baladent partout) et les mêmes règles de hoisting (zone morte temporelle jusqu'à déclaration). La grande différence, c'est qu'une fois initialisée, la variable ne peut plus être réassignée.
Caractéristiques de const
- Obligation d'initialiser : Avec const, tu dois donner une valeur dès la déclaration. Pas le choix.
const x;tout seul, ça ne passe pas (erreur SyntaxError directe). Il faut faireconst x = 42;par exemple. - Pas de réassignation : Une fois ta constante définie, impossible de lui donner une nouvelle valeur plus tard. Si tu tentes le coup, tu récolteras une erreur dès l'exécution (TypeError en mode "assignment to constant variable"). La valeur est figée… enfin, nuance, la référence est figée.
- Portée de bloc & pas de redéclaration : Comme let, un const vit dans le bloc où il est déclaré, et tu ne peux pas déclarer deux fois le même nom dans la même portée. C'est du propre.
Regarde ce petit exemple pour bien voir la différence avec une variable modifiable :
javascript
On définit PI comme constant, on peut l'utiliser sans souci tant qu'on ne cherche pas à la changer. Au moment où on essaye de faire PI = 3.15, JavaScript nous arrête net : interdiction de modifier une constante.
Note : Constante ne veut pas dire immuable à 100%. Si la valeur est un objet ou un tableau, tu pourras toujours modifier l'intérieur de l'objet/du tableau. Ce qui ne bouge pas, c'est la variable elle-même (la référence en mémoire). Par exemple :
javascript
Ici, on a pu changer name dans l'objet utilisateur malgré le const. En revanche, réaffecter carrément utilisateur vers un autre objet est interdit. Donc, const = constant, pas modifiable, mais uniquement au niveau de la variable elle-même. Il ne faut pas confondre constance de la variable et immutabilité de la valeur.
javascript
Avec un tableau, c'est la même logique : on peut modifier son contenu (ajouter, supprimer, modifier des éléments) car on ne change que l'intérieur du tableau. Mais on ne peut pas réassigner la variable fruits vers un nouveau tableau. C'est comme si le tableau était une boîte : on peut changer ce qu'il y a dedans, mais pas remplacer la boîte elle-même.
En pratique, const est idéal pour toutes les valeurs qui ne doivent pas changer : par exemple une configuration, une référence, etc. C'est très utilisé et ça rend le code plus robuste (on sait que cette variable ne bougera pas). D'ailleurs, les bonnes pratiques recommandent souvent d'utiliser const par défaut, et de passer à let uniquement quand on sait que la valeur devra changer. Quant à var… eh bien, à moins d'une raison particulière, on l'oublie 😉.
Portée des variables : globale, fonction ou bloc ?
Parlons un peu plus de portée (scope). La portée détermine "où" une variable est accessible dans ton code. Il y a principalement trois niveaux de portée en JavaScript :
Types de portée
- Portée globale : Une variable est globale lorsqu'elle est déclarée en dehors de toute fonction (dans le script principal ou la console). Elle est alors accessible partout (dans n'importe quelle fonction ou bloc). ⚠️ Attention, en dehors d'un module, un var global devient une propriété de l'objet global (comme window dans un navigateur), tandis que let et const créent des variables globales "propres" (non attachées à window). Dans un module ES6, les variables top-level ne fuient pas sur l'objet global du tout.
- Portée de fonction : C'est la portée créée à l'intérieur d'une fonction. Une variable var déclarée dans une fonction ne sera visible qu'à l'intérieur de cette fonction (et ses sous-fonctions éventuellement). On ne la voit pas en dehors. Idem pour let et const déclarés dans une fonction : ils restent locaux à cette fonction.
- Portée de bloc : C'est la portée limitée à un bloc délimité par { ... } (par exemple à l'intérieur d'un if, d'une boucle for, d'un while, ou tout simplement un bloc isolé). let et const sont block-scoped, c'est-à-dire accessibles uniquement dans le bloc où on les définit (et ses sous-blocs). var en revanche n'a pas de portée de bloc : son bloc de référence est la fonction englobante (ou global si aucune fonction).
En résumé : var est limité à la fonction (ou global), alors que let et const sont limités au bloc courant. Cette différence règle pas mal de pièges classiques. Par exemple :
javascript
Ici, la boucle avec var laisse traîner i en dehors, avec sa valeur finale 3. La boucle avec let, elle, nettoie j en sortant : impossible d'y accéder après coup. En pratique, ça veut dire que tu peux avoir plusieurs variables let nommées pareil dans des blocs différents sans interférence, là où un var unique se serait partagé entre tous ces blocs.
Hoisting : quand JavaScript joue à cache-cache
Passons au hoisting, ce mécanisme de hissage des déclarations. C'est souvent source de confusion (et de blagues en réunion), alors clarifions. Le hoisting, c'est le fait que le moteur JavaScript "déplace" en quelque sorte les déclarations de variables et de fonctions en tête de leur scope au moment de l'exécution. En réalité, rien n'est vraiment déplacé dans ton code, c'est juste que JavaScript alloue la mémoire pour tes variables en amont, avant d'exécuter pas à pas.
Comportement du hoisting
- Pour var : La déclaration est hoistée ET initialisée à undefined par défaut. Du coup, tu peux tout à fait utiliser une variable var avant sa ligne de déclaration, elle existe déjà (avec la valeur undefined jusqu'à ce que la vraie assignation arrive plus bas). C'est un comportement légitime du langage, mais qui peut provoquer des bugs bien sournois si tu n'y prends garde.
- Pour let et const : La déclaration est hoistée aussi (le moteur sait qu'il y a une variable dans ce bloc), mais aucune initialisation par défaut n'est faite. La variable est placée en zone morte temporelle (Temporal Dead Zone) jusqu'à ce que la ligne de déclaration soit exécutée. Concrètement, cela signifie que si tu tentes de lire ou utiliser cette variable avant sa déclaration effective, JavaScript va jeter une erreur (ReferenceError). Autrement dit, la variable existe en théorie, mais tu n'y as pas accès tant qu'on n'a pas exécuté sa déclaration/initialisation.
Faisons un petit test de hoisting dans la pratique :
javascript
Au moment du premier console.log, la variable monVar a bien été créée en mémoire via hoisting, mais sa valeur par défaut est undefined (puisqu'on ne l'a pas encore assignée). Aucune erreur, juste undefined qui s'affiche. Ensuite on assigne "Bonjour", et la seconde fois on obtient la valeur attendue.
Voyons maintenant avec let :
javascript
Ici, le premier console.log provoque une ReferenceError. Pourquoi ? Parce que monLet est hoisté sans initialisation. Il est dans sa zone morte temporelle, inaccessible. JavaScript refuse de nous donner une valeur qui n'existe pas encore réellement. Une fois qu'on exécute let monLet = "Hello";, la variable sort de sa zone morte et obtient "Hello". Le second console.log marche alors parfaitement.
On le voit bien : var se comporte comme un ninja en remontant sa déclaration en haut et en se donnant la valeur undefined en douce, tandis que let/const jouent la sécurité et empêchent tout usage tant qu'ils ne sont pas initialisés. Moralité : évite de coder en comptant sur le hoisting, c'est un coup à se faire des nœuds au cerveau (et à sortir des undefined inattendus). Mieux vaut déclarer tes variables en haut de leurs scopes et garder ton code clair. Si tu oublies, le comportement de let/const te rappellera à l'ordre avec une erreur, là où var t'aurait laissé patauger avec un undefined.
Ah, et tant qu'on y est, note que les fonctions déclarées classiquement (function maFonction() \{ ... \}) sont aussi hoistées entièrement. Tu peux appeler une fonction déclarée plus bas dans ton code, JavaScript la connaîtra déjà. Mais attention, les fonctions fléchées ou expressions de fonction assignées à des variables, elles, suivent les règles de hoisting de var/let selon le mot-clé utilisé (sujet pour un autre jour 😉).
Un petit détour par TypeScript : encore plus de clarté
Avant de se quitter, parlons vite fait de TypeScript. Si tu commences à utiliser TypeScript (le super-set de JavaScript qui ajoute le typage statique), tu retrouveras nos trois compères var, let et const. Bonne nouvelle : leurs comportements en portée et hoisting restent exactement les mêmes qu'en JavaScript pur, puisque TypeScript se compile en JavaScript standard. Cependant, TypeScript apporte sa touche perso côté typage, et ça vaut le coup d'œil, surtout pour let et const.
TypeScript et les variables
- Inférence de type : TypeScript devine le type de tes variables en fonction de la valeur initiale. Si tu écris
let fruit = "pomme";, TypeScript va inférer que fruit est de type string. Si tu écrislet age = 25;, il comprendra que age est un number, etc. Pour let, il reste assez large : la variable peut changer, donc le type est celui de base (string, number, etc.). - Constantes et types littéraux : Là où c'est fun, c'est qu'avec const, TypeScript va inférer un type littéral. Autrement dit, la valeur étant constante, TS peut se permettre de dire « cette variable a pour type exactement cette valeur ». Par exemple :
typescript
La variable couleur est de type "rouge" et pas juste string. Intérêt ? Si plus loin ton code attend précisément la valeur "rouge" ou "bleu" (par exemple un paramètre qui ne peut être que l'une de ces couleurs), et que tu as const couleur = "rouge", tu pourras le passer sans souci. Avec un let couleur = "rouge" (type string large), TypeScript aurait râlé car "rouge" n'est qu'une possibilité parmi tous les string. En bref, const en TypeScript permet d'être ultra précis sur le type quand c'est utile. C'est comme un bonus qui renforce encore la notion de constante.
- Même discipline : TypeScript va t'encourager à utiliser let et const exactement comme en ES6. D'ailleurs, la plupart des projets TypeScript bannissent carrément var (il existe même une règle de linter pour ça). Et si tu t'amuses à utiliser une variable avant déclaration, TypeScript te criera dessus à la compilation – bien avant que le runtime JavaScript ne le fasse. Par exemple, si tu écris du TypeScript avec
console.log(maVar); var maVar = 3;, il te signalera que tu as un problème de portée ou d'ordre. Idem pour un let utilisé trop tôt, TS connaît les règles de hoisting et te protégera (autant que possible) des bêtises.
En résumé, TypeScript ne change pas les règles fondamentales de var/let/const, mais il les renforce avec le filet de sécurité du typage. Tu gagnes en clarté et en erreurs attrapées plus tôt. Retiens surtout que const en TypeScript, c'est doublement gagnant : une variable non réassignable et un type littéral précis – que demander de plus ?
Conclusion : quand utiliser quoi sans se tromper
On a fait le tour du propriétaire, alors qui est le gagnant du match var vs let vs const ? Sans grande surprise : en 2025, le duo let & const l'emporte haut la main pour écrire du code propre et maintenable. Voici un petit récap épicé de conseils pratiques :
Bonnes pratiques
- Utilise const dès que possible : Pour toute variable dont la valeur n'a pas besoin de changer, const est ton meilleur ami. Ça verrouille la réassignation et ça indique clairement à tout le monde (y compris à toi dans 6 mois) que cette valeur ne bougera pas. Parfait pour les configurations, les références fixes, etc.
- Si tu dois changer la valeur, utilise let : Besoin d'incrémenter un compteur, d'accumuler un résultat, de stocker une valeur intermédiaire modifiable ? let est là pour ça. Il se comporte bien (portée de bloc, pas de piège de hoisting imprévu), et il te laisse la flexibilité de changer la valeur.
- Évite var sauf cas tordu : Honnêtement, var peut pratiquement disparaître de ton vocabulaire courant. Il survit pour des raisons de rétro-compatibilité et quelques scénarios spécifiques, mais dans du code moderne il n'a plus trop la cote. Si tu bosses sur un vieux projet ou que tu dois manipuler le scope global volontairement (des choses qu'on évite en général), ok, var pourrait pointer le bout de son nez. Sinon, tu peux le ranger au musée des reliques JS 😄.
- En cas de doute, explique-toi : Si un jour tu utilises var et qu'on te demande pourquoi en réunion, aie une bonne raison et explique-la (par exemple, « je dois déclarer cette variable en global pour que d'anciennes scripts y accèdent »). Mais 99% du temps, tu n'auras pas besoin de cette gymnastique : let et const couvriront tous tes besoins sans sourciller.
Voilà, tu sais maintenant quand utiliser var, let ou const en JavaScript, et même un peu comment ça se passe côté TypeScript. Plus question de confondre nos trois compères ni de te faire chambrer en revue de code parce que tu as sorti un var de ton chapeau sans raison valable. Allez, file coder sereinement, et n'oublie pas : const autant que possible, let si nécessaire, et var… le moins possible ! Bon dev !