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

Tutoriel sur la boucle for...of en JavaScript

JavaScript

Découvrez pourquoi la boucle for...of devrait être votre choix par défaut pour l’itération en JavaScript. Apprenez à l’utiliser avec les tableaux, les chaînes, les Maps, les Sets et les itérables personnalisés. Maîtrisez le destructuring, explorez les itérateurs complémentaires et libérez la puissance de l’évaluation paresseuse avec les générateurs.

Guide complet de la boucle for...of en JavaScript
Boucle for...of en JavaScript : parcourez les tableaux, les chaînes, les Maps, les Sets et plus encore

JavaScript a toujours offert plusieurs façons d’itérer sur des données. De la boucle numérique classique for héritée de C à la méthode forEach, les développeurs ont eu de nombreuses options. Cependant, ES2015 a introduit un véritable changement de jeu : la boucle for...of.

Cette boucle reste encore sous-utilisée par de nombreux développeurs, alors qu’elle remplace élégamment la plupart des patterns d’itération traditionnels. Elle fonctionne de manière fluide avec les tableaux, les chaînes, les Maps, les Sets, les NodeLists et tout objet qui implémente le protocole itérable. Plus important encore, elle vous donne un contrôle total sur la quantité de l’itérable consommée, ce qui la rend idéale pour l’évaluation paresseuse et les séquences infinies.

Bref historique des boucles JavaScript

Avant de plonger dans for...of, passons rapidement en revue les boucles que JavaScript a proposées au fil des années :

La boucle for numérique

La boucle for traditionnelle, empruntée à C et aux langages similaires, repose sur une approche basée sur un index :

for (var index = 0, len = items.length; index < len; ++index) {
  // Work with items[index]
}

Bien que puissante, cette syntaxe peut être verbeuse et source d’erreurs. Vous devez gérer l’index manuellement, mettre en cache la longueur pour les performances et penser à incrémenter le compteur.

La boucle for...in

La boucle for...in est spécifiquement conçue pour parcourir les propriétés énumérables d’un objet :

var person = { first: 'John', last: 'Smith' };
for (var prop in person) {
  console.log(prop, '=', person[prop]);
  // Outputs: 'first' = 'John', 'last' = 'Smith'
}

Notez que for...in ne sert pas à parcourir les valeurs d’un tableau, mais les propriétés d’un objet. L’utiliser sur des tableaux peut entraîner des résultats inattendus.

Les boucles while et do...while

Ces boucles conditionnelles existent dans de nombreux langages de programmation. while évalue la condition avant chaque itération, tandis que do...while l’évalue après, garantissant au moins une exécution.

Qu’est-ce que for...of ?

ES2015 a formalisé un concept crucial : les itérables. Un itérable est un objet qui implémente le protocole itérable, défini via la méthode Symbol.iterator. De nombreux objets natifs JavaScript sont itérables : tableaux, chaînes, Maps, Sets, NodeLists, et plus encore.

La boucle for...of est le mécanisme principal pour consommer des itérables avec un contrôle complet. Vous pouvez consommer exactement ce dont vous avez besoin, et la quantité peut être déterminée dynamiquement ou algorithmiquement.

Lorsque vous travaillez avec des tableaux, vous vous intéressez généralement aux valeurs, et non aux indices. La boucle for...of rend cela naturel :

// Old way: verbose and index-focused
for (var index = 0, len = items.length; index < len; ++index) {
  var item = items[index];
  // Work with item
}

// Modern way: clean and value-focused
for (const item of items) {
  // Work with item directly
}

Comme pour toutes les boucles, vous pouvez utiliser break, continue et return dans for...of. Mais elle est plus polyvalente que les boucles numériques : elle fonctionne même sans indices (comme avec les Sets), et vous n’avez pas besoin de vous soucier de mettre en cache la longueur.

Utiliser const avec for...of

Remarquez que nous avons utilisé const pour la variable de boucle ? C’est parce que nous ne réassignons rien : nous travaillons directement avec chaque valeur à mesure qu’elle provient de l’itérateur. Il n’y a pas d’index à incrémenter manuellement, donc const est le choix naturel et aide à éviter les réaffectations accidentelles.

// ✅ Good: const prevents reassignment
for (const item of items) {
  // item is read-only in this scope
}

// ❌ Avoid: let is unnecessary unless you need to reassign
for (let item of items) {
  item = transform(item); // Only affects local variable, not the array
}

Le destructuring à la volée

Lorsque votre itérateur renvoie plusieurs valeurs (comme les entrées d’une Map), vous pouvez les déstructurer directement dans la déclaration de la boucle. Cela rend le code beaucoup plus clair :

const map = new Map([
  [1, 'one'],
  [2, 'two'],
  [3, 'three'],
]);

// Without destructuring: verbose
for (const pair of map) {
  console.log(pair[0], '=', pair[1]);
}

// With destructuring: clean and readable
for (const [key, value] of map) {
  console.log(key, '=', value);
}

Ce pattern fonctionne avec tout itérable qui renvoie des tableaux ou des tuples. Vous pouvez déstructurer de manière aussi profonde que nécessaire :

const entries = [
  ['user', { id: 1, name: 'Alice' }],
  ['admin', { id: 2, name: 'Bob' }],
];

for (const [role, { id, name }] of entries) {
  console.log(`${role}: ${name} (${id})`);
}

Itérateurs complémentaires

De nombreux itérables fournissent des itérateurs supplémentaires au-delà de leur itération par défaut. La plupart en proposent au moins trois : keys(), values() et entries(). Pour les objets sans clés (comme les Sets), les clés sont simplement les valeurs elles-mêmes.

Par exemple, si vous souhaitez parcourir un tableau tout en récupérant l’indice, vous pouvez utiliser entries() :

const fruits = ['apple', 'banana', 'cherry'];

// Get both index and value
for (const [index, fruit] of fruits.entries()) {
  console.log(`${index}: ${fruit}`);
}
// Output:
// 0: apple
// 1: banana
// 2: cherry

// Or iterate over just the keys (indices)
for (const index of fruits.keys()) {
  console.log(index);
}
// Output: 0, 1, 2

// Or just the values (same as default)
for (const fruit of fruits.values()) {
  console.log(fruit);
}
// Output: apple, banana, cherry

Les Maps et les Sets fournissent aussi ces méthodes, ce qui facilite le travail avec leurs structures de données spécifiques :

const userMap = new Map([
  ['alice', { role: 'admin' }],
  ['bob', { role: 'user' }],
]);

// Iterate over entries (default)
for (const [username, user] of userMap) {
  console.log(`${username}: ${user.role}`);
}

// Iterate over just keys
for (const username of userMap.keys()) {
  console.log(username);
}

// Iterate over just values
for (const user of userMap.values()) {
  console.log(user.role);
}

L’évaluation paresseuse : la vraie puissance

La véritable force de for...of réside dans sa capacité à fonctionner avec l’évaluation paresseuse. Comme elle consomme les itérables de manière incrémentale plutôt que d’un seul coup, elle constitue la principale façon de travailler avec des calculs paresseux, y compris les séquences infinies.

Prenons un générateur qui produit la suite de Fibonacci :

function* fibonacci() {
  let [current, next] = [1, 1];
  while (true) {
    yield current;
    [current, next] = [next, current + next];
  }
}

Cette suite ne se termine jamais. Si vous essayez de la convertir en tableau avec Array.from() ou avec l’opérateur spread, votre programme se bloquera. En revanche, vous pouvez la consommer sans risque avec for...of :

// Get first few terms via destructuring
const [a, b, c, d, e] = fibonacci();
// a === 1, b === 1, c === 2, d === 3, e === 5

// Consume until a condition is met
for (const term of fibonacci()) {
  if (term > 100) break;
  console.log(term);
}
// Output: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89

Ce pattern permet de créer toutes sortes de primitives d’évaluation paresseuse. Par exemple, voici une fonction take qui limite le nombre d’éléments consommés :

function* take(count, iter) {
  if (count === 0) return;
  for (const term of iter) {
    yield term;
    if (--count <= 0) break;
  }
}

// Get first 10 Fibonacci numbers
for (const num of take(10, fibonacci())) {
  console.log(num);
}
// Output: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55

Cette approche de l’évaluation paresseuse est fondamentale pour les concepts de programmation fonctionnelle et ressemble à la façon dont des bibliothèques comme RxJS travaillent avec les observables.

Considérations de performance

Vous trouverez de nombreux benchmarks contradictoires en ligne comparant différents types de boucles. La plupart ne sont pas représentatifs des cas réels. Gardez ces deux points à l’esprit :

  • Pour la plupart des tableaux (jusqu’à des millions d’éléments), la différence de performance est négligeable.
  • Lorsqu’elle n’est pas transpillée (avec la prise en charge native du navigateur), for...of a des performances similaires aux autres boucles.

Il est extrêmement rare que vous deviez revenir à une boucle numérique for ou à while pour des raisons de performance. Les gains en lisibilité et en flexibilité de for...of dépassent de loin les micro-optimisations dans la grande majorité des cas.

Remplacer les anciens patterns

Vous pouvez désormais remplacer la grande majorité de vos itérations traditionnelles par for...of. Cela inclut :

  • Les boucles for numériques lorsqu’il s’agit de parcourir des tableaux
  • forEach() lorsque vous avez besoin de break ou continue
  • les méthodes each() de jQuery/Lodash
  • for...in lorsqu’il est mal utilisé sur des tableaux
// ❌ Old: forEach (can't break early)
items.forEach(item => {
  if (item.shouldStop) return; // Only exits callback, not the loop
  process(item);
});

// ✅ New: for...of (can break early)
for (const item of items) {
  if (item.shouldStop) break; // Exits the entire loop
  process(item);
}

// ❌ Old: jQuery each
$.each(items, function(index, item) {
  // ...
});

// ✅ New: for...of
for (const item of items) {
  // ...
}

Prise en charge navigateur et transpilation

La boucle for...of est prise en charge nativement dans :

  • Firefox 13+
  • Chrome 38+
  • Opera 25+
  • Edge 12+
  • Safari 7+
  • Node.js 0.12+

Pour les environnements plus anciens, Babel et TypeScript peuvent transpiler for...of en code compatible. Sur de très grands tableaux (1M+ d’éléments), le code transpillé peut avoir un certain surcoût de performance, mais cela dépend fortement du moteur JavaScript, de votre algorithme et du contexte.

Exemples concrets

Exemple 1 : traitement de réponses API

async function processUsers() {
  const response = await fetch('/api/users');
  const users = await response.json();
  
  for (const user of users) {
    if (user.status === 'inactive') continue;
    await updateUserProfile(user);
  }
}

Exemple 2 : travailler avec des collections DOM

// NodeList is iterable
const buttons = document.querySelectorAll('.action-button');

for (const button of buttons) {
  button.addEventListener('click', handleClick);
}

// Works with HTMLCollection too (after conversion)
const divs = Array.from(document.getElementsByTagName('div'));
for (const div of divs) {
  div.classList.add('processed');
}

Exemple 3 : itérables personnalisés

class NumberRange {
  constructor(start, end) {
    this.start = start;
    this.end = end;
  }

  *[Symbol.iterator]() {
    for (let i = this.start; i <= this.end; i++) {
      yield i;
    }
  }
}

const range = new NumberRange(1, 5);
for (const num of range) {
  console.log(num);
}
// Output: 1, 2, 3, 4, 5

Bonnes pratiques

  • Utilisez const par défaut : puisque vous ne réassignez pas la variable de boucle, const est le choix naturel.
  • Déstructurez quand c’est pertinent : si votre itérable renvoie des tableaux ou des objets, déstructurez-les directement dans la déclaration de boucle.
  • Utilisez entries() pour les indices : quand vous avez besoin à la fois de l’indice et de la valeur d’un tableau, utilisez array.entries().
  • Préférez for...of à forEach : quand vous avez besoin de break ou continue, for...of est votre allié.
  • Exploitez l’évaluation paresseuse : utilisez des générateurs et for...of pour les séquences infinies et les calculs à la demande.

Conclusion

La boucle for...of est l’une des fonctionnalités les plus élégantes et les plus puissantes de JavaScript. Elle simplifie l’itération, fonctionne avec une grande variété de structures de données et libère la puissance de l’évaluation paresseuse grâce aux générateurs.

Bien qu’elle ne soit pas encore aussi largement adoptée qu’elle le devrait, for...of devrait être votre choix par défaut pour l’itération en JavaScript moderne. Elle remplace la plupart des boucles numériques, élimine souvent le besoin de forEach et fournit une syntaxe claire et lisible qui fonctionne avec tous les types d’itérables.

Commencez à utiliser for...of dans votre code dès aujourd’hui. Votre futur vous-même, et vos coéquipiers, vous remercieront pour ce code plus clair et plus maintenable.

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