Table of Contents
La difference entre var et let en JavaScript
==============================================
Introduction : L’evolution des declarations de variables en JavaScript
L’histoire des declarations de variables en JavaScript est fascinante et reflète l’evolution du langage lui-meme. Pendant pres de deux decennies, var etait la seule option disponible pour declarer des variables. Cette simplicite apparente cachait cependant de nombreux pieges qui ont cause d’innombrables bugs dans les applications web.
En 1995, lorsque Brendan Eich a cree JavaScript en seulement 10 jours, les decisions de conception etaient guidees par la rapidite plutot que par la robustesse. Le comportement de var, notamment le hoisting et le function scope, semblait intuitif a l’epoque mais s’est avere problematique a mesure que les applications devenaient plus complexes.
L’arrivee d’ECMAScript 2015 (ES6) a marque un tournant majeur. Cette version a introduit let et const, deux nouvelles facons de declarer des variables qui resolvent les problemes historiques de var. Aujourd’hui, ces declarations modernes sont devenues la norme dans le developpement JavaScript professionnel.
Comprendre les differences entre var, let et const n’est pas qu’une question de syntaxe. C’est une competence fondamentale qui vous permettra d’ecrire du code plus previsible, plus maintenable et moins sujet aux bugs. Que vous travailliez sur une application React, un backend Node.js ou un script vanilla JavaScript, cette connaissance est essentielle.
Dans cet article approfondi, nous allons explorer chaque type de declaration en detail, comprendre leurs mecanismes internes, et decouvrir les meilleures pratiques adoptees par la communaute JavaScript moderne. Vous apprendrez non seulement les differences techniques, mais aussi pourquoi ces differences existent et comment les exploiter efficacement.
var en detail : le heritage de JavaScript
Le hoisting complet avec var
Le hoisting (ou “hissage” en francais) est l’un des comportements les plus deroutants de JavaScript pour les developpeurs venant d’autres langages. Avec var, la declaration de la variable est “hissee” au sommet de son scope, mais pas son initialisation.
console.log(message); // undefined (pas d'erreur !)
var message = "Bonjour";
console.log(message); // "Bonjour"
Ce code est interprete par JavaScript comme :
var message; // Declaration hissee au sommet
console.log(message); // undefined
message = "Bonjour"; // Initialisation reste a sa place
console.log(message); // "Bonjour"
Ce comportement peut mener a des bugs subtils car une variable peut etre utilisee avant d’etre explicitement declaree dans le code. Le moteur JavaScript ne signale aucune erreur, rendant le debogage difficile.
function exemplePiege() {
console.log(compteur); // undefined, pas d'erreur
for (var compteur = 0; compteur < 5; compteur++) {
// ...
}
console.log(compteur); // 5 - la variable existe toujours !
}
Le function scope : portee limitee a la fonction
Contrairement a de nombreux langages qui utilisent le block scope, var est confine uniquement au scope de la fonction dans laquelle il est declare. Les blocs comme if, for, while ne creent pas de nouveau scope pour var.
function demonstrationFunctionScope() {
var x = 1;
if (true) {
var x = 2; // Meme variable que celle au-dessus !
console.log(x); // 2
}
console.log(x); // 2 - la valeur a ete modifiee
}
demonstrationFunctionScope();
Cette caracteristique rend le code difficile a raisonner, surtout dans les fonctions longues avec de nombreux blocs conditionnels.
function traitementComplexe(donnees) {
var resultat = [];
for (var i = 0; i < donnees.length; i++) {
if (donnees[i].actif) {
var temp = donnees[i].valeur * 2;
resultat.push(temp);
}
}
// Attention : i et temp sont accessibles ici !
console.log(i); // donnees.length
console.log(temp); // derniere valeur calculee ou undefined
return resultat;
}
La redeclaration permise : source de confusion
Avec var, vous pouvez redeclarer la meme variable plusieurs fois sans erreur. JavaScript ne vous avertira pas que vous ecrasez potentiellement une variable existante.
var utilisateur = "Alice";
console.log(utilisateur); // "Alice"
// Plus tard dans le code...
var utilisateur = "Bob"; // Pas d'erreur !
console.log(utilisateur); // "Bob"
// Encore plus tard...
var utilisateur = 42; // Toujours pas d'erreur !
console.log(utilisateur); // 42
Dans un fichier de plusieurs centaines de lignes, ce comportement peut conduire a des bugs tres difficiles a detecter.
Le probleme classique des closures dans les boucles
C’est peut-etre le bug le plus celebre lie a var. Lorsque vous creez des closures dans une boucle, toutes les closures partagent la meme variable.
// Le probleme classique
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
// Affiche : 3, 3, 3 (pas 0, 1, 2 comme attendu !)
Pourquoi ce comportement ? Parce que var est function-scoped, il n’existe qu’une seule variable i partagee par toutes les callbacks. Au moment ou les callbacks s’executent (apres 1 seconde), la boucle est terminee et i vaut 3.
// Solution historique avec une IIFE (Immediately Invoked Function Expression)
for (var i = 0; i < 3; i++) {
(function(index) {
setTimeout(function() {
console.log(index);
}, 1000);
})(i);
}
// Affiche : 0, 1, 2
Cette solution fonctionnait mais etait verbeuse et difficile a lire. Heureusement, let resout elegamment ce probleme.
let en detail : la declaration moderne
Le block scope : une portee intuitive
let introduit le block scope en JavaScript. Une variable declaree avec let n’existe que dans le bloc ou elle est definie (delimite par des accolades {}).
function demonstrationBlockScope() {
let x = 1;
if (true) {
let x = 2; // Variable differente !
console.log(x); // 2
}
console.log(x); // 1 - la variable originale est preservee
}
Ce comportement correspond a ce que font la plupart des autres langages de programmation comme Java, C++, ou Python.
function traitementAvecLet(donnees) {
let resultat = [];
for (let i = 0; i < donnees.length; i++) {
if (donnees[i].actif) {
let temp = donnees[i].valeur * 2;
resultat.push(temp);
}
// temp n'est pas accessible ici
}
// i n'est pas accessible ici non plus
// console.log(i); // ReferenceError !
return resultat;
}
La Temporal Dead Zone (TDZ) : protection contre l’acces premature
La Temporal Dead Zone (Zone Mortelle Temporelle) est une caracteristique de securite de let. Une variable existe dans son scope des le debut du bloc, mais elle ne peut pas etre accedee avant sa declaration.
{
// Debut de la TDZ pour maVariable
console.log(maVariable); // ReferenceError: Cannot access 'maVariable' before initialization
let maVariable = "Hello"; // Fin de la TDZ
console.log(maVariable); // "Hello"
}
La TDZ aide a detecter les erreurs de programmation. Si vous essayez d’utiliser une variable avant de l’avoir declaree, JavaScript vous le signale immediatement.
function exempleTDZ() {
// La TDZ commence ici pour 'valeur'
let autreVariable = valeur; // ReferenceError !
// meme si 'valeur' sera declaree plus bas
let valeur = 42;
}
Attention, la TDZ s’applique aussi aux parametres de fonction par defaut :
// Erreur : 'b' est dans la TDZ quand 'a' est evalue
function erreurTDZ(a = b, b = 1) {
return a + b;
}
// Correct : 'a' est deja initialise quand 'b' est evalue
function correctTDZ(a = 1, b = a) {
return a + b;
}
Pas de redeclaration : securite renforcee
Contrairement a var, let ne permet pas de redeclarer une variable dans le meme scope. Cette restriction previent les erreurs accidentelles.
let utilisateur = "Alice";
// Plus tard dans le meme scope...
let utilisateur = "Bob"; // SyntaxError: Identifier 'utilisateur' has already been declared
Cependant, vous pouvez toujours reassigner une nouvelle valeur :
let utilisateur = "Alice";
utilisateur = "Bob"; // OK, c'est une reassignation, pas une redeclaration
console.log(utilisateur); // "Bob"
Solution elegante au probleme des closures
Avec let, chaque iteration de boucle cree une nouvelle variable. Les closures capturent donc la bonne valeur.
// Solution avec let
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
// Affiche : 0, 1, 2 (comme attendu !)
Chaque iteration cree une nouvelle liaison pour i, donc chaque callback capture sa propre copie de la variable.
// Exemple plus concret : creation de gestionnaires d'evenements
const boutons = document.querySelectorAll('.bouton');
for (let index = 0; index < boutons.length; index++) {
boutons[index].addEventListener('click', function() {
console.log('Bouton ' + index + ' clique');
});
}
// Chaque bouton affiche son propre index
const : l’immutabilite en JavaScript
Immutabilite de la reference
const declare une constante dont la reference ne peut pas etre modifiee. Une fois assignee, vous ne pouvez pas reassigner une nouvelle valeur.
const PI = 3.14159;
PI = 3.14; // TypeError: Assignment to constant variable
const NOM_APPLICATION = "MonApp";
NOM_APPLICATION = "AutreApp"; // TypeError
const partage les memes caracteristiques que let pour le scope et la TDZ :
{
console.log(CONSTANTE); // ReferenceError (TDZ)
const CONSTANTE = 42;
}
// CONSTANTE n'est pas accessible ici (block scope)
Objets et tableaux avec const
Attention ! const rend la reference immutable, pas la valeur elle-meme. Pour les objets et tableaux, vous pouvez modifier leur contenu.
const utilisateur = {
nom: "Alice",
age: 25
};
// Modification du contenu : OK
utilisateur.age = 26;
utilisateur.email = "alice@example.com";
console.log(utilisateur); // { nom: "Alice", age: 26, email: "alice@example.com" }
// Reassignation de la reference : ERREUR
utilisateur = { nom: "Bob" }; // TypeError
Meme principe pour les tableaux :
const nombres = [1, 2, 3];
// Modifications du contenu : OK
nombres.push(4);
nombres[0] = 10;
console.log(nombres); // [10, 2, 3, 4]
// Reassignation : ERREUR
nombres = [5, 6, 7]; // TypeError
Object.freeze() pour l’immutabilite profonde
Si vous avez besoin d’un objet veritablement immutable, utilisez Object.freeze() :
const config = Object.freeze({
apiUrl: "https://api.example.com",
timeout: 5000,
maxRetries: 3
});
config.timeout = 10000; // Silencieusement ignore (ou TypeError en mode strict)
console.log(config.timeout); // 5000
Attention, Object.freeze() n’est pas recursif (shallow freeze). Pour une immutabilite profonde, vous devez geler recursivement :
function deepFreeze(obj) {
Object.keys(obj).forEach(prop => {
if (typeof obj[prop] === 'object' && obj[prop] !== null) {
deepFreeze(obj[prop]);
}
});
return Object.freeze(obj);
}
const configProfonde = deepFreeze({
api: {
url: "https://api.example.com",
headers: {
'Content-Type': 'application/json'
}
}
});
Tableau comparatif : var vs let vs const
| Caracteristique | var | let | const |
|---|---|---|---|
| Scope | Function scope | Block scope | Block scope |
| Hoisting | Oui (undefined) | Oui (TDZ) | Oui (TDZ) |
| TDZ | Non | Oui | Oui |
| Redeclaration | Permise | Interdite | Interdite |
| Reassignation | Permise | Permise | Interdite |
| Initialisation obligatoire | Non | Non | Oui |
| Disponibilite | Toutes versions | ES6+ | ES6+ |
Exemples illustrant chaque difference
// === SCOPE ===
function testScope() {
if (true) {
var varVariable = "var";
let letVariable = "let";
const constVariable = "const";
}
console.log(varVariable); // "var"
// console.log(letVariable); // ReferenceError
// console.log(constVariable); // ReferenceError
}
// === HOISTING ===
function testHoisting() {
console.log(varVariable); // undefined
// console.log(letVariable); // ReferenceError (TDZ)
// console.log(constVariable); // ReferenceError (TDZ)
var varVariable = "var";
let letVariable = "let";
const constVariable = "const";
}
// === REDECLARATION ===
var x = 1;
var x = 2; // OK
let y = 1;
// let y = 2; // SyntaxError
const z = 1;
// const z = 2; // SyntaxError
// === REASSIGNATION ===
var a = 1;
a = 2; // OK
let b = 1;
b = 2; // OK
const c = 1;
// c = 2; // TypeError
Dans les boucles : exemples detailles
La boucle for classique
// Avec var : une seule variable partagee
console.log("=== Boucle avec var ===");
for (var i = 0; i < 3; i++) {
console.log("Iteration:", i);
}
console.log("Apres boucle, i =", i); // 3
// Avec let : variable confinee a la boucle
console.log("=== Boucle avec let ===");
for (let j = 0; j < 3; j++) {
console.log("Iteration:", j);
}
// console.log("Apres boucle, j =", j); // ReferenceError
Closures dans les boucles : comparaison complete
// Probleme avec var
console.log("=== setTimeout avec var ===");
var fonctionsVar = [];
for (var i = 0; i < 3; i++) {
fonctionsVar.push(function() {
return i;
});
}
console.log(fonctionsVar[0]()); // 3
console.log(fonctionsVar[1]()); // 3
console.log(fonctionsVar[2]()); // 3
// Solution avec let
console.log("=== setTimeout avec let ===");
var fonctionsLet = [];
for (let j = 0; j < 3; j++) {
fonctionsLet.push(function() {
return j;
});
}
console.log(fonctionsLet[0]()); // 0
console.log(fonctionsLet[1]()); // 1
console.log(fonctionsLet[2]()); // 2
Boucle for...of et for...in
const fruits = ['pomme', 'banane', 'orange'];
// for...of avec let (recommande)
for (let fruit of fruits) {
console.log(fruit);
}
// for...of avec const (encore mieux si pas de reassignation)
for (const fruit of fruits) {
console.log(fruit);
}
// for...in pour les objets
const personne = { nom: 'Alice', age: 25, ville: 'Paris' };
for (const cle in personne) {
console.log(`${cle}: ${personne[cle]}`);
}
Boucles imbriquees
// Exemple pratique : matrice
const matrice = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
for (let ligne = 0; ligne < matrice.length; ligne++) {
for (let colonne = 0; colonne < matrice[ligne].length; colonne++) {
console.log(`Position [${ligne}][${colonne}] = ${matrice[ligne][colonne]}`);
}
}
// Les variables ligne et colonne ne fuient pas hors de leurs boucles respectives
Bonnes pratiques : guide du developpeur moderne
Regle d’or : const par defaut
La communaute JavaScript moderne recommande d’utiliser const par defaut, et let uniquement quand une reassignation est necessaire.
// RECOMMANDE : const par defaut
const API_URL = 'https://api.example.com';
const utilisateur = { nom: 'Alice', role: 'admin' };
const processData = (data) => data.map(x => x * 2);
// let seulement si reassignation necessaire
let compteur = 0;
let resultat;
function incrementer() {
compteur++; // Reassignation necessaire
}
function calculer(valeur) {
resultat = valeur * 2; // Reassignation necessaire
return resultat;
}
Pourquoi privilegier const ?
- Intention claire : Le lecteur sait immediatement que la variable ne changera pas
- Moins de bugs : Impossible de reassigner accidentellement
- Optimisation : Les moteurs JavaScript peuvent optimiser les constantes
- Refactoring plus sur : Deplacer du code est plus facile
// Code avec const : intention claire
const calculerTotal = (items) => {
const TVA = 0.20;
const sousTotal = items.reduce((acc, item) => acc + item.prix, 0);
const total = sousTotal * (1 + TVA);
return total;
};
// Le lecteur sait que TVA, sousTotal et total ne changeront pas
Quand utiliser let ?
Utilisez let uniquement dans ces cas :
// 1. Compteurs
let compteur = 0;
while (condition) {
compteur++;
}
// 2. Accumulateurs dans des boucles
let somme = 0;
for (const nombre of nombres) {
somme += nombre;
}
// 3. Variables qui changent d'etat
let estConnecte = false;
function connecter() {
estConnecte = true;
}
// 4. Resultats conditionnels
let message;
if (utilisateur.admin) {
message = "Bienvenue, administrateur";
} else {
message = "Bienvenue, utilisateur";
}
Ne jamais utiliser var
Il n’y a pratiquement aucune raison d’utiliser var dans du code moderne. Les seules exceptions sont :
- Maintenance de code legacy (ancien)
- Compatibilite avec de tres vieux navigateurs (sans transpilation)
// A EVITER
var ancienCode = "problematique";
// PREFERER
const codeModerne = "propre";
let siNecessaire = "mutation";
Pieges courants et comment les eviter
Piege 1 : Confondre immutabilite de reference et de valeur
// PIEGE : croire que const rend l'objet immutable
const config = { debug: true };
config.debug = false; // Fonctionne !
// SOLUTION : utiliser Object.freeze si necessaire
const configFrozen = Object.freeze({ debug: true });
configFrozen.debug = false; // Ignore silencieusement
Piege 2 : Oublier la TDZ
// PIEGE : utiliser une variable avant sa declaration
function piege() {
console.log(valeur); // ReferenceError !
const valeur = 42;
}
// SOLUTION : toujours declarer en haut du scope
function correct() {
const valeur = 42;
console.log(valeur);
}
Piege 3 : Declarer dans le mauvais scope
// PIEGE : variable inaccessible apres le bloc
if (condition) {
const resultat = calculer();
}
console.log(resultat); // ReferenceError !
// SOLUTION : declarer avant si necessaire apres
let resultat;
if (condition) {
resultat = calculer();
}
console.log(resultat); // OK
Piege 4 : Variables globales accidentelles
// PIEGE : oublier le mot-cle de declaration
function piege() {
variable = "globale"; // Cree une variable globale !
}
// SOLUTION : toujours utiliser const ou let
function correct() {
const variable = "locale";
}
// MIEUX : activer le mode strict
"use strict";
function strictMode() {
variable = "erreur"; // ReferenceError en mode strict
}
Piege 5 : const dans les switch sans blocs
// PIEGE : collision de declarations
switch (valeur) {
case 1:
const message = "un"; // Meme scope !
break;
case 2:
const message = "deux"; // SyntaxError: identifier already declared
break;
}
// SOLUTION : utiliser des blocs explicites
switch (valeur) {
case 1: {
const message = "un";
console.log(message);
break;
}
case 2: {
const message = "deux"; // OK, scope different
console.log(message);
break;
}
}
Conclusion
Comprendre les differences entre var, let et const est fondamental pour tout developpeur JavaScript moderne. Ces trois mots-cles representent l’evolution du langage vers plus de securite et de previsibilite.
Resume des points cles :
var: A eviter. Function-scoped, hoiste, redeclarable. Source de nombreux bugs historiques.let: Pour les variables qui doivent etre reassignees. Block-scoped, protege par la TDZ.const: A privilegier par defaut. Memes caracteristiques quelet, mais la reference est immutable.
La regle simple a retenir :
- Utilisez
constpar defaut - Utilisez
letuniquement si vous devez reassigner - N’utilisez jamais
vardans du code nouveau
En appliquant ces principes, vous ecrirez du code JavaScript plus propre, plus maintenable et moins sujet aux bugs. La transition de var vers const/let est l’une des ameliorations les plus significatives apportees par ES6, et maitriser ces concepts vous placera parmi les developpeurs JavaScript competents.
N’hesitez pas a revisiter cet article lorsque vous avez des doutes sur quelle declaration utiliser. Avec la pratique, le choix entre const et let deviendra instinctif, et vous apprecierez la clarte et la securite qu’ils apportent a votre code.
In-Article Ad
Dev Mode
Mahmoud DEVO
Senior Full-Stack Developer
I'm a passionate full-stack developer with 10+ years of experience building scalable web applications. I write about Vue.js, Node.js, PostgreSQL, and modern DevOps practices.
Enjoyed this article?
Subscribe to get more tech content delivered to your inbox.
Related Articles
La déclaration const dans JavaScript : comprendre les blocs
Voici une proposition de métadescription qui répond aux exigences : "Découvrez comment utiliser const et let pour créer des variables avec un nouveau champ d'a
Manipulation des dates en JavaScript : UTC, conversion et formatage
Guide complet sur les dates JavaScript : conversion en chaine, creation de dates UTC, methodes setUTC et bonnes pratiques pour eviter les problemes de fuseaux.
La comparaison équivalente en JavaScript : une analyse appro
Here's a compelling meta description that summarizes the main value proposition, includes a subtle call-to-action, and meets the 150-160 character requirement: