// Web

Fonctions fléchées et closures en JavaScript

La fonction fléchée JavaScript (arrow function) est partout dans le code moderne : callbacks de map, gestionnaires d’événements, promesses… Sa syntaxe compacte a fait oublier qu’elle n’est pas qu’un raccourci pour function : elle traite le mot-clé this différemment. En parallèle, les closures — cette capacité d’une fonction à se souvenir de son environnement — sont l’un des mécanismes les plus puissants et les plus mal compris du langage. Ce guide couvre les deux, avec tous les cas et tous les pièges.

La syntaxe des fonctions fléchées

Une fonction fléchée s’écrit sans le mot-clé function, avec une flèche => entre les paramètres et le corps. Voici toutes les variantes de syntaxe.

// Fonction classique
const double = function (x) {
  return x * 2;
};

// Équivalent fléché, retour explicite
const double2 = (x) => {
  return x * 2;
};

// Retour implicite : pas d'accolades, pas de return
const double3 = (x) => x * 2;

// Un seul paramètre : parenthèses facultatives
const double4 = x => x * 2;

// Aucun paramètre : parenthèses obligatoires
const saluer = () => "Bonjour";

// Plusieurs paramètres
const somme = (a, b) => a + b;

// Renvoyer un objet littéral : entourer d'accolades avec parenthèses
const creerUser = (nom) => ({ nom, actif: true });

Cette concision brille avec les méthodes de tableau :

const nombres = [1, 2, 3, 4];
const carres = nombres.map(n => n * n);        // [1, 4, 9, 16]
const pairs = nombres.filter(n => n % 2 === 0); // [2, 4]

Le comportement de this

C’est la différence fondamentale. Une fonction classique définit son propre this, qui dépend de comment elle est appelée. Une fonction fléchée n’a pas de this propre : elle hérite de celui du contexte englobant au moment de sa définition (on parle de this lexical).

const compteur = {
  valeur: 0,
  elements: [1, 2, 3],

  // Fonction classique : this perdu dans le callback
  buggyIncrement() {
    this.elements.forEach(function () {
      this.valeur++; // this vaut undefined ici, erreur
    });
  },

  // Fonction fléchée : this reste celui de l'objet
  increment() {
    this.elements.forEach(() => {
      this.valeur++; // this = compteur, fonctionne
    });
  },
};

Cette différence rend les fonctions fléchées idéales comme callbacks, mais inadaptées comme méthodes d’objet ou comme constructeurs.

const objet = {
  nom: "Coder Studio",
  // Piège : this ne pointe PAS sur objet
  direNom: () => console.log(this.nom), // undefined
};

// Une fonction fléchée ne peut pas être un constructeur
const Voiture = () => {};
// new Voiture(); // TypeError: Voiture is not a constructor

Différences avec function : le récapitulatif

Au-delà de this, plusieurs points distinguent les deux formes.

// 1. Pas d'objet arguments — utiliser le rest
const classique = function () {
  return arguments.length; // fonctionne
};
const flechee = (...args) => args.length; // équivalent moderne

// 2. Pas de hoisting : une fonction fléchée dans une const
//    n'existe pas avant sa ligne de déclaration
// direBonjour(); // erreur
const direBonjour = () => "salut";

// 3. Concision et retour implicite (voir plus haut)

En résumé : fonction fléchée pour les callbacks et les fonctions courtes qui héritent du contexte ; function (ou la syntaxe de méthode abrégée) pour les méthodes d’objet, les constructeurs et tout ce qui a besoin de son propre this.

Qu’est-ce qu’une closure ?

Une closure (fermeture) se produit lorsqu’une fonction se souvient des variables de la portée où elle a été créée, même après que cette portée a fini de s’exécuter. Autrement dit, la fonction « capture » son environnement.

function creerCompteur() {
  let compte = 0; // variable locale

  return function () {
    compte++;      // la fonction interne se souvient de compte
    return compte;
  };
}

const compteur = creerCompteur();
console.log(compteur()); // 1
console.log(compteur()); // 2
console.log(compteur()); // 3

La variable compte a survécu à la fin de creerCompteur. Chaque appel à compteur() accède à la même variable, conservée en mémoire par la closure.

Cas d’usage des closures

Les closures ne sont pas une curiosité théorique : elles structurent du code réel.

Encapsulation et données privées

Une closure permet de créer des variables réellement privées, inaccessibles depuis l’extérieur.

function creerCompteBancaire(soldeInitial) {
  let solde = soldeInitial; // privé

  return {
    deposer(montant) {
      solde += montant;
      return solde;
    },
    consulter() {
      return solde;
    },
  };
}

const compte = creerCompteBancaire(100);
compte.deposer(50);       // 150
compte.consulter();        // 150
// Impossible de faire compte.solde = 0 depuis l'extérieur

Fonctions de fabrique (factory)

On génère des fonctions préconfigurées en capturant un paramètre.

function multiplicateurPar(facteur) {
  return (x) => x * facteur; // facteur est capturé
}

const triple = multiplicateurPar(3);
const dixFois = multiplicateurPar(10);

console.log(triple(4));   // 12
console.log(dixFois(4));  // 40

Mémoïsation

Une closure conserve un cache entre les appels pour éviter de recalculer.

function creerMemo() {
  const cache = {};
  return (n) => {
    if (n in cache) return cache[n];
    const resultat = n * n; // calcul coûteux imaginé
    cache[n] = resultat;
    return resultat;
  };
}

const carre = creerMemo();
carre(5); // calculé
carre(5); // renvoyé depuis le cache

Ce patron est courant pour optimiser des calculs ou limiter des appels réseau. Ces techniques se marient bien avec les méthodes de tableau quand on génère des transformateurs réutilisables.

Le piège de la closure en boucle

C’est le bug le plus célèbre lié aux closures. Avec var, toutes les fonctions créées dans la boucle partagent la même variable.

// Problème : var est partagé
const fonctions = [];
for (var i = 0; i < 3; i++) {
  fonctions.push(() => console.log(i));
}
fonctions[0](); // 3 — pas 0 !
fonctions[1](); // 3
fonctions[2](); // 3

Au moment où les fonctions s’exécutent, la boucle est terminée et i vaut 3. Toutes pointent sur la même variable.

La solution moderne tient en un mot : utiliser let, qui crée une nouvelle liaison à chaque itération.

// Solution : let crée un i par tour de boucle
const fonctions = [];
for (let i = 0; i < 3; i++) {
  fonctions.push(() => console.log(i));
}
fonctions[0](); // 0
fonctions[1](); // 1
fonctions[2](); // 2

Avant l’arrivée de let, on contournait le problème avec une IIFE (fonction immédiatement invoquée) qui capturait la valeur courante :

for (var i = 0; i < 3; i++) {
  ((valeur) => {
    // valeur est figé pour cette itération
    setTimeout(() => console.log(valeur), 100);
  })(i);
}
// Affiche 0, 1, 2

À retenir

Les fonctions fléchées offrent une syntaxe concise et un this lexical qui les rend parfaites comme callbacks, mais inaptes comme méthodes ou constructeurs. Les closures, elles, permettent l’encapsulation, les fabriques de fonctions et la mémoïsation en capturant leur environnement. Retenez surtout le duo let + closure pour éviter le piège de boucle, et vous aurez une compréhension solide de deux piliers du JavaScript moderne.

À lire ensuite