Getting started General
Components
Forms
Trends
Utilities
Migrate from v1
  Join us
  JavaScript Articles

Hoisting JavaScript : guide complet du hoisting des variables et des fonctions

JavaScript

Apprenez tout ce que vous devez savoir sur le hoisting JavaScript avec ce guide. Vous saurez comment var, let, const et les declarations de fonctions sont hoistees. Vous comprendrez aussi la Temporal Dead Zone et vous eviterez les pieges les plus courants grace a nos exemples.

Hoisting JavaScript : guide complet du hoisting des variables et des fonctions
Hoisting JavaScript : guide complet du hoisting des variables et des fonctions

Le hoisting JavaScript est l'un des concepts les plus importants a comprendre, surtout si vous travaillez avec ce langage. Il y a une difference avec d'autres langages de programmation. En JavaScript, les declarations de variables et de fonctions sont "hoistees" en haut de leur scope contenant avant l'execution du code. Ce comportement peut mener a des resultats etranges/inattendus s'il est mal compris.

Dans ce guide, nous allons couvrir tout ce que vous devez savoir sur le hoisting JavaScript. Nous allons commencer par expliquer comment les declarations var et function sont hoistees. Ensuite, nous verrons comment let et const se comportent. Enfin, nous verrons comment ecrire un meilleur code JavaScript et eviter les problemes courants.

Qu'est-ce que le hoisting en JavaScript ?

Le hoisting est une specificite JavaScript ou les declarations de variables et de fonctions sont deplacees en haut de leur scope contenant avant l'execution du code. Cela signifie que vous pouvez utiliser des variables et des fonctions avant qu'elles ne soient declarees dans votre code.

Cependant, il est important de comprendre que seules les declarations sont hoistees, pas les initialisations. Pour les variables declarees avec var, elles sont hoistees et initialisees a undefined. Pour les fonctions, toute la declaration de fonction est hoistee, ce qui la rend disponible dans tout le scope.

Exemple basique de hoisting

// This code works because of hoisting
console.log(x); // undefined (not an error!)
var x = 5;

// This is how JavaScript interprets the code above:
var x;          // Declaration is hoisted
console.log(x); // undefined
x = 5;          // Initialization stays in place

Comment fonctionne le hoisting des fonctions

Les declarations de fonctions sont entierement hoistees en JavaScript. Cela signifie que la declaration et le corps de la fonction sont deplaces en haut du scope. Cela signifie que vous pouvez appeler des fonctions avant qu'elles ne soient definies dans votre code.

Hoisting des declarations de fonctions

// This works because function declarations are hoisted
greet(); // "Hello, World!"

function greet() {
  console.log("Hello, World!");
}

// This is how JavaScript interprets it:
function greet() {
  console.log("Hello, World!");
}
greet(); // "Hello, World!"

Function expression vs function declaration

Il est crucial de comprendre que les function expressions ne sont PAS hoistees. Seules les function declarations sont hoistees. C'est une distinction importante :

// Function declaration - HOISTED
sayHello(); // "Hello!"

function sayHello() {
  console.log("Hello!");
}

// Function expression - NOT HOISTED
sayGoodbye(); // TypeError: sayGoodbye is not a function

var sayGoodbye = function() {
  console.log("Goodbye!");
};

// How JavaScript interprets the function expression:
var sayGoodbye;        // Declaration hoisted, initialized with undefined
sayGoodbye();          // TypeError: sayGoodbye is undefined
sayGoodbye = function() {
  console.log("Goodbye!");
};

Pourquoi le hoisting des fonctions est utile

Le hoisting des fonctions vous permet d'organiser votre code en placant l'algorithme principal en haut et les fonctions helper en dessous, ce qui peut ameliorer la lisibilite :

function processItems(items) {
  // Main algorithm at the top
  return items
    .filter(itemMatches)
    .map(transformItem);

  // Helper functions below
  function itemMatches(item) {
    return item.status === 'active';
  }

  function transformItem(item) {
    return {
      id: item.id,
      name: item.name.toUpperCase()
    };
  }
}

// This works because both functions are hoisted
const result = processItems([
  { id: 1, name: 'Alice', status: 'active' },
  { id: 2, name: 'Bob', status: 'inactive' }
]);
console.log(result); // [{ id: 1, name: 'ALICE' }]

Hoisting des variables avec var

Les variables declarees avec var sont hoistees en haut de leur scope de fonction (ou du scope global si elles sont declarees hors d'une fonction). Cependant, seule la declaration est hoistee, pas l'initialisation. La variable est initialisee a undefined tant que la ligne d'assignation n'est pas executee.

Hoisting var basique

function example() {
  console.log(x); // undefined (not an error!)
  var x = 10;
  console.log(x); // 10
}

// How JavaScript interprets this:
function example() {
  var x;           // Declaration hoisted
  console.log(x);  // undefined
  x = 10;          // Initialization stays in place
  console.log(x);  // 10
}

Le probleme avec le hoisting de var

Le comportement de hoisting de var peut mener a des bugs inattendus, surtout dans des boucles avec des operations asynchrones :

const people = ['Alice', 'Bob', 'Claire', 'David'];

for (var i = 0; i < people.length; i++) {
  setTimeout(function() {
    console.log(people[i]); // Outputs: undefined (4 times)
  }, 100);
}

// Why this happens:
// The variable 'i' is hoisted to the function scope
// By the time the setTimeout callbacks execute, the loop has finished
// and 'i' equals people.length (4), which is out of bounds

// How JavaScript interprets this:
var i;  // Hoisted to function scope
for (i = 0; i < people.length; i++) {
  setTimeout(function() {
    console.log(people[i]); // All callbacks see the same 'i' value
  }, 100);
}
// After loop completes, i = 4, so all callbacks log undefined

La solution historique : IIFE

Avant ES2015, les developpeurs utilisaient des Immediately Invoked Function Expressions (IIFE) pour creer un nouveau scope pour chaque iteration :

const people = ['Alice', 'Bob', 'Claire', 'David'];

for (var i = 0; i < people.length; i++) {
  (function(index) {
    setTimeout(function() {
      console.log(people[index]); // Works correctly
    }, 100);
  })(i);
}

// Output: Alice, Bob, Claire, David (in order)

Scope de bloc et variables ES2015+

ES2015 a introduit let et const, qui ont un comportement de hoisting fondamentalement different par rapport a var. Ces nouveaux mots-cles fournissent un scope au niveau du bloc et ne sont pas hoistes de la meme maniere.

Scope de bloc avec let et const

Les variables declarees avec let et const sont scopees au bloc dans lequel elles sont declarees, pas a toute la fonction :

function demonstrateScope() {
  if (true) {
    var functionScoped = 'I am function scoped';
    let blockScoped = 'I am block scoped';
    const alsoBlockScoped = 'I am also block scoped';
  }

  console.log(functionScoped); // "I am function scoped" (works!)
  console.log(blockScoped);    // ReferenceError: blockScoped is not defined
  console.log(alsoBlockScoped); // ReferenceError: alsoBlockScoped is not defined
}

Resoudre le probleme de boucle avec let

Le probleme de boucle avec var se resout facilement en utilisant let, qui cree un nouveau binding a chaque iteration :

const people = ['Alice', 'Bob', 'Claire', 'David'];

for (let i = 0; i < people.length; i++) {
  setTimeout(function() {
    console.log(people[i]); // Works correctly!
  }, 100);
}

// Output: Alice, Bob, Claire, David (in order)

// Why this works:
// Each iteration creates a new 'i' binding in its own block scope
// Each setTimeout callback captures its own 'i' value

La Temporal Dead Zone (TDZ)

La Temporal Dead Zone (TDZ) est la periode entre l'entree dans un scope et la declaration effective d'une variable. Pendant ce temps, la variable ne peut pas etre accessible. Cela s'applique aux declarations let, const et class.

Comprendre la Temporal Dead Zone

// Temporal Dead Zone starts here
console.log(myVar); // ReferenceError: Cannot access 'myVar' before initialization

let myVar = 10;
// Temporal Dead Zone ends here

// This is different from var:
console.log(myVar2); // undefined (not an error)
var myVar2 = 10;

Pourquoi la Temporal Dead Zone existe

La TDZ aide a detecter tot les erreurs de programmation en empechant l'acces aux variables avant leur initialisation. Cela rend le code plus previsible et plus facile a debugger :

function example() {
  // TDZ starts
  console.log(typeof myLet); // ReferenceError (not "undefined")
  
  let myLet = 'initialized';
  // TDZ ends
}

// Compare with var:
function example2() {
  console.log(typeof myVar); // "undefined" (no error)
  var myVar = 'initialized';
}

Ordre et priorite du hoisting

Quand plusieurs declarations existent dans le meme scope, JavaScript les hoiste dans un ordre specifique :

  1. Les declarations de fonctions sont hoistees en premier
  2. Les declarations de variables (var) sont hoistees en second
  3. Les function expressions ne sont pas entierement hoistees (seule la declaration de variable)

Exemple d'ordre de hoisting

// What you write:
console.log(typeof myFunc); // "function"
console.log(typeof myVar);  // "undefined"

function myFunc() {
  return 'I am a function';
}

var myVar = 'I am a variable';

// How JavaScript interprets it:
function myFunc() {  // Function hoisted first
  return 'I am a function';
}
var myVar;            // Variable declaration hoisted second

console.log(typeof myFunc); // "function"
console.log(typeof myVar);  // "undefined"
myVar = 'I am a variable';

Conflits de nom entre fonction et variable

Quand une fonction et une variable partagent le meme nom, la declaration de fonction est prioritaire :

console.log(typeof myName); // "function" (not "undefined")

function myName() {
  return 'I am a function';
}

var myName = 'I am a variable';

console.log(typeof myName); // "string" (after assignment)

// How JavaScript interprets this:
function myName() {  // Function hoisted first
  return 'I am a function';
}
var myName;          // Variable declaration (ignored, name already exists)

console.log(typeof myName); // "function"
myName = 'I am a variable'; // Assignment overwrites the function
console.log(typeof myName); // "string"

Bonnes pratiques et JavaScript moderne

Comprendre le hoisting est essentiel, mais les bonnes pratiques de JavaScript moderne recommandent d'eviter les pieges lies au hoisting :

1. Eviter var - utiliser let et const a la place

Depuis ES2015, var devrait etre evite au profit de let et const :

// ❌ Avoid var
function badExample() {
  if (true) {
    var x = 10;
  }
  console.log(x); // 10 (unexpected function scope)
}

// ✅ Use let or const
function goodExample() {
  if (true) {
    let x = 10;
  }
  console.log(x); // ReferenceError (expected block scope)
}

2. Utiliser const par defaut

L'approche moderne JavaScript est d'utiliser const par defaut et n'utiliser let que si vous devez reassigner la variable :

// ✅ Use const by default
const userName = 'Alice';
const userAge = 25;
const userRoles = ['admin', 'user'];

// Only use let when reassignment is needed
let counter = 0;
counter = 1; // Valid reassignment

// const prevents accidental reassignment
const maxItems = 100;
maxItems = 200; // TypeError: Assignment to constant variable

3. Declarer les variables en haut de leur scope

Meme si le hoisting rend possible l'utilisation de variables avant leur declaration, il est preferable de les declarer en haut de leur scope pour plus de clarte :

// ❌ Confusing due to hoisting
function confusing() {
  console.log(value);
  var value = 10;
}

// ✅ Clear and explicit
function clear() {
  const value = 10;
  console.log(value);
}

4. Utiliser les function declarations pour beneficier du hoisting

Les function declarations peuvent etre utiles quand vous voulez organiser le code avec l'algorithme principal en haut :

// ✅ Good use of function hoisting
function processData(data) {
  // Main algorithm at top
  const filtered = data.filter(isValid);
  const transformed = filtered.map(transform);
  return transformed;

  // Helper functions below
  function isValid(item) {
    return item.status === 'active';
  }

  function transform(item) {
    return { id: item.id, name: item.name.toUpperCase() };
  }
}

Pieges courants du hoisting et solutions

Piege 1 : acceder aux variables avant leur declaration

// ❌ Problem with var
function problem() {
  console.log(x); // undefined (confusing!)
  var x = 5;
}

// ✅ Solution: Use let/const
function solution() {
  // console.log(x); // ReferenceError (clear error message)
  const x = 5;
  console.log(x); // 5
}

Piege 2 : variables de boucle dans des callbacks async

// ❌ Problem with var
const items = ['a', 'b', 'c'];
for (var i = 0; i < items.length; i++) {
  setTimeout(() => console.log(items[i]), 100);
}
// Output: undefined, undefined, undefined

// ✅ Solution: Use let
const items = ['a', 'b', 'c'];
for (let i = 0; i < items.length; i++) {
  setTimeout(() => console.log(items[i]), 100);
}
// Output: a, b, c

Piege 3 : function expression vs declaration

// ❌ Function expression not hoisted
myFunction(); // TypeError: myFunction is not a function

var myFunction = function() {
  console.log('Hello');
};

// ✅ Function declaration is hoisted
myFunction(); // "Hello"

function myFunction() {
  console.log('Hello');
}

Hoisting des classes

ES2015 a introduit les declarations class, qui se comportent comme let et const :

// ❌ Class is in TDZ before declaration
const instance = new MyClass(); // ReferenceError: Cannot access 'MyClass' before initialization

class MyClass {
  constructor() {
    this.value = 10;
  }
}

// ✅ Use class after declaration
class MyClass {
  constructor() {
    this.value = 10;
  }
}

const instance = new MyClass(); // Works correctly

Exemples concrets

Exemple 1 : organiser le code avec le hoisting des fonctions

// Main algorithm at the top, helpers below
function validateUserData(userData) {
  // Main validation logic
  if (!isValidEmail(userData.email)) {
    return { valid: false, error: 'Invalid email' };
  }

  if (!isValidAge(userData.age)) {
    return { valid: false, error: 'Invalid age' };
  }

  if (!isValidName(userData.name)) {
    return { valid: false, error: 'Invalid name' };
  }

  return { valid: true };

  // Helper functions (hoisted, so they work above)
  function isValidEmail(email) {
    return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
  }

  function isValidAge(age) {
    return typeof age === 'number' && age >= 0 && age <= 120;
  }

  function isValidName(name) {
    return typeof name === 'string' && name.trim().length >= 2;
  }
}

// Usage
const result = validateUserData({
  email: '[email protected]',
  age: 25,
  name: 'Alice'
});
console.log(result); // { valid: true }

Exemple 2 : eviter les pieges courants des boucles

// Modern approach with let
function attachEventListeners(buttons) {
  for (let i = 0; i < buttons.length; i++) {
    buttons[i].addEventListener('click', function() {
      console.log(`Button ${i} clicked`); // Each button logs its correct index
    });
  }
}

// Even better: Use forEach
function attachEventListenersModern(buttons) {
  buttons.forEach((button, index) => {
    button.addEventListener('click', function() {
      console.log(`Button ${index} clicked`);
    });
  });
}

Resume : points cles

  • Les function declarations sont entierement hoistees et peuvent etre appelees avant d'apparaitre dans le code
  • Les declarations var sont hoistees mais initialisees a undefined
  • let et const sont dans la Temporal Dead Zone jusqu'a leur ligne de declaration
  • Les function expressions ne sont pas hoistees (seule la declaration de variable l'est)
  • Utilisez const par defaut, let quand une reassignment est necessaire, et evitez var
  • Le hoisting des fonctions peut etre utile pour organiser le code avec la logique principale en haut
  • Le scope de bloc avec let et const evite de nombreux bugs courants

Conclusion

Le hoisting JavaScript est un concept fondamental que chaque developpeur JavaScript devrait comprendre. Meme s'il peut mener a des comportements inattendus avec var, les bonnes pratiques JavaScript modernes avec let et const aident a eviter ces problemes.

Le hoisting des fonctions peut etre utile pour organiser le code, en vous permettant de placer l'algorithme principal en haut d'une fonction et les fonctions helpers en dessous. Cependant, il est important de comprendre les differences entre function declarations et function expressions.

En comprenant le comportement du hoisting, la Temporal Dead Zone et en suivant les bonnes pratiques JavaScript modernes, vous pouvez ecrire du code plus previsible et plus maintenable. Pensez a utiliser const par defaut, let quand c'est necessaire, et evitez var dans du code JavaScript moderne.

Commencez à créer avec Axentix

Prêt à créer des sites web exceptionnels ? Commencez avec le framework Axentix dès aujourd'hui.

Commencer

Articles similaires