Impératif vs. Déclaratif : Quelle est la meilleure façon de boucler en JavaScript ?

En développement, nous passons notre temps à manipuler des listes de données. Pour ce faire, il existe deux grandes philosophies : l'approche impérative et l'approche déclarative. Comprendre la différence et savoir quand utiliser l'une ou l'autre peut radicalement améliorer la qualité et la lisibilité de votre code.

Le style impératif : Vous donnez les ordres

L'approche impérative consiste à expliquer à l'ordinateur comment réaliser une tâche, étape par étape. C'est la méthode la plus "classique", celle que l'on apprend souvent en premier.

Pensez aux boucles for ou while. Vous initialisez un compteur, vous définissez une condition d'arrêt et vous décrivez précisément comment passer d'un élément à l'autre et quoi en faire.

Exemple : Doubler chaque nombre d'un tableau.

const numbers = [1, 2, 3, 4];
const doubled = []; // On a besoin d'un tableau intermédiaire

for (let i = 0; i < numbers.length; i++) {
  const number = numbers[i];
  doubled.push(number * 2);
}

// doubled vaut maintenant [2, 4, 6, 8]
  • Avantages : Contrôle total, performances brutes souvent imbattables.
  • Inconvénients : Verbeux, l'intention (le "quoi") est noyée dans la logique (le "comment").

Le style déclaratif : Vous exprimez votre besoin

Avec l'approche déclarative, vous ne décrivez plus le comment, mais simplement ce que vous voulez obtenir comme résultat. Vous déléguez l'exécution à des fonctions conçues pour ça.

Les méthodes de tableau comme map, filter et reduce sont les parfaits exemples de cette approche.

Exemple : Doubler chaque nombre d'un tableau avec map.

const numbers = [1, 2, 3, 4];

const doubled = numbers.map(number => number * 2);

// doubled vaut maintenant [2, 4, 6, 8]

Le code est plus concis et l'intention est limpide : "Je veux un nouveau tableau où chaque nombre de l'original a été multiplié par deux."

  • map() : Transforme chaque élément d'un tableau et retourne un nouveau tableau de même taille.
  • filter() : Sélectionne les éléments selon une condition et retourne un nouveau tableau avec uniquement ces éléments.
  • reduce() : Applique une fonction à tous les éléments pour les réduire à une seule valeur (un nombre, une chaîne, un objet...).

Avantages :

  • Lisibilité : Le code exprime directement l'intention.
  • Prévisibilité : Ces fonctions retournent de nouveaux tableaux sans modifier l'original (immuabilité), ce qui évite les bugs liés aux modifications inattendues (effets de bord).
  • Compréhension : Il est plus simple de chaîner les opérations (.map().filter()...).

Inconvénients : Une très légère perte de performance due à la création de fonctions et de tableaux intermédiaires.


Le cas particulier de forEach : une fausse bonne idée ?

forEach ressemble à une méthode déclarative, mais son usage se rapproche de l'impératif. Son but n'est pas de transformer des données, mais d'exécuter une action (un effet de bord) pour chaque élément.

Le principal reproche fait à forEach dans le développement moderne est qu'il encourage la modification de variables externes au lieu de favoriser la création de nouvelles données. Cela peut rendre le code plus difficile à suivre et à déboguer.

Comme le souligne cet article d'ÆFLASH, si votre objectif est de créer une nouvelle valeur, map, filter ou reduce sont presque toujours de meilleurs choix. Si vous avez absolument besoin d'un effet de bord, une boucle for...of est souvent plus explicite sur cette intention.


La règle d'or : Lisibilité d'abord, optimisation si nécessaire ✅

Les deux approches sont valables, mais voici une ligne directrice simple et efficace :

  1. Par défaut, utilisez l'approche déclarative (map, filter, reduce). Votre code sera plus propre, plus facile à lire et à maintenir pour vous et votre équipe.
  2. Si, et seulement si, vous identifiez un goulet d'étranglement dans une portion de code (après avoir mesuré les performances !), envisagez de la réécrire avec une boucle impérative (for, while) pour gagner en vitesse.

Ne sacrifiez pas la clarté pour un gain de performance hypothétique.

Pour aller plus loin : les bibliothèques utilitaires 🚀

Des bibliothèques comme Lodash ou Ramda poussent cette logique déclarative encore plus loin. Elles offrent des dizaines de fonctions utilitaires optimisées pour manipuler des données de manière claire, concise et fonctionnelle. Elles sont une excellente option pour les projets qui nécessitent beaucoup de transformations de données.