Que vous soyez un développeur JavaScript débutant ou un expert cherchant à vraiment apprendre les subtilités du langage, une question peut vous perturber :
var
,let
,const
- quelles sont les différences entre ces trois mots-clés en JavaScript ?
Certains développeurs JavaScript s’en tiennent à une règle d’or pour utiliser const
à moins qu’une variable ne doive être réaffectée à l’avenir. Dans ce cas, ils utilisent let
.
Cependant, parfois ce n’est pas toujours le cas, et vous pouvez rencontrer du code qui n’utilise que var
.
Commençons !
La portée (ou en anglais - scope)
Pour commencer notre enquête sur le monde de la syntaxe JS, nous devons d’abord en savoir plus sur la portée (ou en anglais - scope).
La portée définit l’accessibilité des variables pendant la durée de vie d’un programme. En ce qui concerne JavaScript, il existe une portée globale, une portée de fonction et une portée de bloc :
- Portée globale - la portée accessible n’importe où tout au long du programme.
- Portée de la fonction - la portée au sein d’une définition de fonction.
- Portée du bloc - la portée entre { } comme dans le corps d’une boucle for.
Prenons l’exemple suivant :
// La portée globale
const globalScopeVariable = "global"
function laPortéeDeLaFonction() {
const functionScopeVariable = "function"
// La portée du bloc
{
const blockScopeVariable = "block"
// on peut accéder à globalScopeVariable, à functionScopeVariable et à blockScopeVariable
}
// on peut accéder à globalScopeVariable, et à functionScopeVariable
}
// on peut accéder à globalScopeVariable
Comme nous pouvons le voir, la portée contrôle l’accessibilité de certaines variables. Nous disons qu’une certaine variable est limitée (ou en anglais - “scoped”) à un certain type de portée (c’est-à-dire que blockScopeVariable
est limitée à la portée au bloc dans lequel elle se trouve).
La relation entre l’accessibilité des variables et la portée peut fonctionner un peu comme une poupée russe.
Par exemple, la portée globale encapsule à la fois la portée de la fonction et celle du bloc. La fonction et la portée du bloc fonctionnent de manière quelque peu interchangeable. Par exemple - c’est parfaitement OK pour écrire le programme suivant :
function scopeOne() {
{
function scopeTwo() {
{
let high = "five"
}
}
}
}
Comme vous pouvez le voir - la fonction scopeTwo
est définie dans un bloc
var
et let
.
var ou let - Hoisting (Hissage)
Commençons par définir deux variables. L’un avec le mot-clé let
et l’autre avec le mot-clé var
:
let myLet = "Pomme"
var myVar = "Orange"
console.log({ myLet, myVar })
// SORTIE: { myLet: 'Pomme', myVar: 'Orange' }
// **Protip** - l'enregistrement des variables en tant qu'objet augmente la lisibilité, car les paires clé-valeur sont enregistrées dans la console.
Créons maintenant une fonction. En faisant cela, nous montrerons une différence clé en enregistrant ces deux variables sur la console avant qu’elles soient définies.
function myFunction() {
console.log({myVar})
console.log({myLet})
let myLet = "Pomme"
var myVar = "Orange"
}
myFunction();
L’exécution de ceci nous donne l’erreur suivante :
{ myVar: undefined }
Thrown:
ReferenceError: myLet Cannot access 'myLet' before initialization at myFunction
…Quoi ? 🤔 myVar
est undefined
et myLet
renvoie une ReferenceError
indiquant que myLet
n’a pas encore été défini.
Que se passe-t-il ici ?
myVar
est hissé (hoisted) au sommet de la portée de la fonction, et est donc considéré comme accessible tout au long de la fonction même avant qu’il ne soit défini avec une valeur appropriée. En fait, il prend la valeur de undefined
(pas défini).
Avant de continuer, examinons comment notre code JavaScript est construit et exécuté, et à partir de là, nous comprendrons mieux ce concept de hissage (hoisting).
Création et exécution de code JavaScript
Lors de l’interprétation du code JavaScript, il y a 2 phases.
⚠️ Ceci est une généralisation. Chaque moteur JS est différent.
- La phase de création correspond au moment où l’interpréteur JavaScript alloue de l’espace pour les déclarations de variables et de fonctions en mémoire.
- La phase d’exécution correspond au moment où l’interpréteur JavaScript commence à exécuter notre code ligne par ligne.
Alors, que s’est-il exactement passé pendant “la phase de création” dans l’extrait d’exemple ci-dessus ? myVar
a été défini avec une valeur par défaut de undefined
.
myLet
est différent. Il est alloué de la mémoire, et donc techniquement hoisted (hissé) mais il est considéré comme étant dans un état appelé zone morte temporelle (temporal dead zone). Dans cet état, myLet
se voit allouer de la mémoire, mais elle est techniquement inaccessible.
Donc, pour résumer notre première différence entre let
et var
:
var
peut être accédé et écrasé n’importe où dans une portée de fonction ou une portée globale en raison du hissage (hoisting).let
présente également ce comportement, sauf qu’il n’est pas accessible, car il se trouve dans un état nommé zone morte temporelle (temporal dead zone).
var vs let - Différences de portée
Une autre différence est le comportement de let
et var
en ce qui concerne la portée. Jetons un œil à cet exemple :
var myVar = "FirstVar"
let myLet = "FirstLet"
function myFunction() {
console.log({myVar})
console.log({myLet})
{
console.log({myVar})
console.log({myLet})
var myVar = "SecondVar"
let myLet = "SecondLet"
}
}
myFunction();
Ce qui nous renvoie ceci :
{ myVar: undefined }
{ myLet: 'FirstLet' }
{ myVar: undefined }
Uncaught ReferenceError: Cannot access 'myLet' before initialization at myFunction
…Hein ? 🤔 myVar
est undefined
et myLet
est FirstLet
?
Il semble que myLet
ait hérité de la valeur FirstLet
de la portée globale et qu’il soit devenu inaccessible lorsqu’il a été réassigné dans ce bloc.
Ce n’est peut-être pas évident, mais la différence que nous voyons ici est que la variable définie par var
, myVar
, est hissée dans la définition de fonction la plus proche de laquelle elle réside, tandis que let
ne le fait pas . let
est hissé au bloc le plus proche.
C’est pourquoi on nous renvoie que myLet
est inaccessible dans la portée du bloc, mais pas dans la portée de la fonction !
Pour résumer : var
est limité - ou “scoped” - à la fonction et let
est limité au bloc !
Résumé de let/const vs var
Donc, en résumé, les deux principales différences entre let
(ainsi que const
) et var
sont :
- Comment chacun est initialisé dans la ‘phase de création’
var
est initialisé avecundefined
let
/const
sont initialisés en tant que zones mortes temporelles (temporal dead zones) et sont donc considérés comme non accessibles tant qu’ils ne sont pas définis dans la phase d’exécution.
- Comment chacun est défini par rapport à la portée
var
est limité à la fonction.let/const
sont limités au bloc.
Bonus : un mot supplémentaire sur le hissage
Les fonctions peuvent également être hissées. Prenons cet exemple :
catName("Chloe");
function catName(name) {
console.log("My cat's name is " + name);
}
// SORTIE: "My cat's name is Chloe"
Cette déclaration de function
n’est pas comme ce que nous avons vu avec var
où elle renvoie undefined
. En fait, elle est techniquement accessible lorsqu’on l’appelle avant dans le code !
Dans tous les cas, si vous trouvez que c’est ennuyeux - vous pouvez ajouter strict mode
en haut de votre code JavaScript pour désactiver complètement le hissage :
'use strict';