Pour maîtriser la méthode forEach en JavaScript et l’utiliser dans les meilleures conditions :
Vous saurez utiliser ses forces, repérer ses faiblesses, et choisir des boucles plus adaptées selon les cas.
Comment itérer sur un Array
, un Set
, une Map
(en obtenant ses clés), et les autres objets qui ressemble à des tableaux… mais n’en sont pas. Peut-on faire un break
ou continue
comme dans une boucle classique, le forEach
JavaScript est-il synchrone ? Je réponds à toutes vos questions !
- Syntaxe
- Règles
- Exemples
- break
- continue
- Objets proches de Array
- Méthodes plus adaptées
- Boucles alternatives
- Autres exemples
- Questions fréquentes
Syntaxe de forEach
La méthode JavaScript forEach s’applique à un tableau, une map ou un set (et pas seulement un tableau).
forEach
prend en paramètre une fonction callback, et l’execute en lui passant chaque élément de la collection.
La méthode forEach
et la fonction callback acceptent des paramètres supplémentaires :
La fonction callback peut recevoir deux paramètres optionnels :
- L’index en cours d’itération.
D’autant plus pratique que, si besoin de le gérer après coup, il suffit de l’ajouter à la signature du callback.
Dans le cas d’uneMap
, l’index est en faite la clé courante. - La collection sur laquelle s’effectue le
forEach
dans le cas où le callback n’y avait pas accès.
Et le forEach
lui même accepte un second paramètre :
- Une valeur pour
this
que pourra utiliser un callback de type fonction classique (inutile si le callback est une arrow function).

Téléchargez la Référence Rapide forEach
+ en bonus : le Guide Facile des Boucles
Règles
Pas de retour
forEach
n’est pas conçu pour produire une valeur de retour. La méthode retourne undefined
.
Elle n’est donc pas chainable avec d’autres méthodes JavaScript qui arriveraient après forEach
.
Synchrone
Si la fonction callback prends du temps, attention, l’exécution est bloquante (synchrone).
Jusqu’à la fin
Pas moyen de sortir de la boucle avant la fin, ni avec un break
ni avec un return
dans le callback.
Pas moyen d’utiliser continue
, mais un return
peut faire l’affaire.
Modification en cours de route
Dans le cas de modification du tableau ou d’un élément après l’appel de la méthode :
des éléments peuvent ne pas être pris en compte (la méthode ne fait pas de copie du tableau avant d’itérer).
Valeur de this
Pour utiliser this
dans le callback, et si ce dernier est une function
classique, il faut fournir une valeur pour this
en argument de forEach
. Sinon, c’est undefined
qui sera utilisé dans le callback.
Dans le cas où le callback est une arrow function, il n’y a alors pas besoin de spécifier de valeur pour this
.
Exemples JavaScript avec forEach
Array
Itérer sur un Array
, avec une fonction anonyme, est très simple :
const array = [1, 5, 10];
array.forEach(function(currentValue) {
console.log(currentValue);
});
Et c’est encore plus simple avec une fonction en notation « arrow » (dite « fat arrow function ») :
const array = [1, 5, 10];
array.forEach(currentValue => console.log(currentValue));
Map
Une itération sur les clés et valeurs d’une Map
est tout aussi facile.
La clé de chaque élément de la Map
est donnée par l’index reçu en 2ème argument :
const map = new Map();
const keyObject = { desc: 'an object as a key'};
map.set('key 1', 'value 1');
map.set('key 2', 'value 2');
map.set(keyObject, 'value for keyObject');
map.forEach((value, index) =>
console.log(`at key: ${index}, value is: ${value}`));
Set
De même pour parcourir un Set
:
const set = new Set(['a', 'b', 'c']);
set.forEach(value => console.log(value));
Callback exploitant tous les paramètres
Pour utiliser dans le callback l’index courant et le tableau d’origine :
function logItem(value, index, array) {
let log = value + ' at ' + index;
if (index != array.length - 1) {
log += ', ';
}
else {
log += ', done.';
}
console.log(log);
}
const array = ['a', 'b', 'c'];
array.forEach(logItem);
Passage de this
Si le callback est une fonction classique
Si le callback est n’est pas une arrow function, et que le callback a besoin d’accéder à this
, this
doit être fourni à forEach
en 2ème argument :
function Counter() {
this.even = 0;
this.odd = 0;
}
Counter.prototype.count = function (numbers) {
numbers.forEach(function (number) {
if (number % 2 === 0) {
this.even++;
}
else {
this.odd++;
}
}, this);
};
const counter = new Counter();
counter.count([2, 4, 6, 7, 8]);
console.log(`${counter.even} even, ${counter.odd} odd`);
Si le callback est une fonction arrow
L’arrow function peut accéder à this
sans qu’il y ait besoin de le passer en argument (les arrow functions lient la valeur de this
lexicalement) :
function Counter() {
this.even = 0;
this.odd = 0;
}
Counter.prototype.count = function (numbers) {
numbers.forEach((number) => {
if (number % 2 === 0) {
this.even++;
}
else {
this.odd++;
}
} /* no need for a 'this' argument */);
};
const counter = new Counter();
counter.count([2, 4, 6, 7, 8]);
console.log(`${counter.even} even, ${counter.odd} odd`);
Objet JSON
Il est possible de parcourir un objet JSON, en passant par Object.keys
qui renvoie un tableau des propriétés de l’objet.
Puis on accède à chaque valeur en utilisant la propriété comme clé :
const json = {
'propA': 'value A',
'propB': 'value B'
};
Object.keys(json).forEach(function(prop){
console.log(`${prop} : ${json[prop]}`);
});
Mais bon, c’est quand même plus facile avec for..in
:
for (let prop in json) {
console.log(`${prop} : ${json[prop]}`);
};
Tableau « clairsemé »
S’il n’y a pas de donnée présente à un index, la fonction callback n’est pas appelée pour cet index :
const sparse = [0, 10, , 30]; // no value at index 2
sparse.forEach((value, index) =>
console.log(`at index: ${index}, value is: ${value}`));
break
dans un forEach
break
comme dans une boucle classique JavaScript for
ou for..of
.Quant à faire un throw
d’erreur juste pour sortir de la boucle, cela n’est vraiment pas recommandé.
Mais, avec les méthodes cousines, certains cas peuvent être résolus.
Avec la méthode every
Avec every
, les itérations s’arrêtent lorsqu’une condition n’est plus remplie :
const breakable = [1, 5, 10, 15, 20];
function breakAbove10(element) {
const lte10 = element <= 10;
if (lte10) console.log(element);
else console.log('> 10, break');
return lte10;
}
breakable.every(breakAbove10);
Ce qui fourni un comportement comparable à un break
.
Une variante est possible avec some
, comme nous allons le voir.
Avec la méthode some
Les itérations s’arrêtent dès qu’une condition est remplie :
const breakable = [1, 5, 10, 15];
function breakOnEven(element) {
const isEven = element % 2 === 0;
if (isEven) console.log(element + ' is even, break');
else console.log(element + ' is odd');
return isEven;
}
breakable.some(breakOnEven);
continue
dans un forEach
Il n’y a pas non plus d’instruction continue
comme dans une boucle classique for
ou for..of
.
Mais avec un return
dans la fonction callback, il est possible d’obtenir un comportement similaire.
Le code restant du callback n’est pas exécuté, et on passe à l’itération suivante :
const continueable = [1, 5, 10, 15];
function continueOnEven(element) {
const isEven = element % 2 === 0;
if (isEven) return;
console.log(element + ' is odd');
}
continueable.forEach(continueOnEven);
Objets proches de Array
D’autres structures ressemblent à des arrays mais n’en sont pas, notamment les structures proposées par les Browser APIs (c’est à dire par le navigateur, et non par le moteur JS), comme NodeList
et HTMLCollection
.
HTMLCollection
HTMLCollection est retournée par la méthode de Browser API getElementsByClassName.
HTMLCollection
est une liste dynamique (‘live’) d’éléments HTML correspondant au sélecteur passé en argument. Elle est automatiquement mise à jour lorsque le document est modifié.Array
, et ne peut pas être itérée directement avec la méthode forEach
, même avec les browsers récents.Voyons comment itérer sur une HTMLCollection
par d’autres moyens.
Alternatives
Dans les exemples suivants, on utilisera le code HTML suivant :
<div class="element">Element 1</div>
<div class="element">Element 2</div>
<div class="element">Element 3</div>
et on obtient la HTMLCollection
contenant les 3 tags ainsi :
const htmlCollection = document.getElementsByClassName('element');
Bon, nous allons pouvoir appliquer sur cette collection une première alternative pour itérer : en passant par le prototype
de Array
.
Array.prototype.forEach.call
Cette approche a le mérite de fonctionner dans tous les navigateurs :
Array.prototype.forEach.call(htmlCollection, function(element) {
console.log(element);
});
ou variante un peu plus concise :
[].forEach.call(htmlCollection, function(element) {
console.log(element);
});
Boucle for
classique
Le grand classique, pas très lisible, mais qui fonctionne également dans tous les navigateurs :
for (let i = 0; i < htmlCollection.length; i++) {
console.log(htmlCollection[i]);
}
Conversion en Array
, puis forEach
On peut aussi d’abord convertir la HTMLCollection
en Array
, surtout si on a besoin de la réutiliser sous cette forme, et la parcourir ensuite.
Il y a trois façons de la convertir en tableau :
Array.prototype.slice.call
Fonctionne dans tous les navigateurs (sauf Internet Explorer < 11) :
const collectionAsArray = Array.prototype.slice.call(htmlCollection);
collectionAsArray.forEach(function(element) {
console.log(element);
});
ou un peu mieux :
const elementsAsArray = [].slice.call(htmlCollection);
// etc...
Spread opearator
L’opérateur spread est plus concis que slice
, mais il lui manque le support d’Internet Explorer, même en version 11 :
const collectionAsArray = [...htmlCollection];
collectionAsArray.forEach(element => console.log(element));
Array.from
Là aussi, il manque le support d’Internet Explorer, même en version 11 :
const collectionAsArray = Array.from(htmlCollection);
collectionAsArray.forEach(element => console.log(element));
Boucle for..of
Le code idéal, car concis et lisible ?
Par contre, apparue avec la version ES6 de JavaScript, la boucle for..of
n’est supportée que par les navigateurs evergreen.
Et encore, Edge ne l’a pas implémentée sur la HTMLCollection
, mais c’est prévu pour l’automne 2018 :
for (const element of htmlCollection) {
console.log(element);
}
C’est beau, quand même…
Conclusion
Il y a beaucoup (trop !) d’approches possibles en JavaScript pour parcourir les une HTMLCollection
, alors si vous ne deviez retenir qu’une chose, la voici :
- Pour supporter tous les navigateurs :
Bouclefor
classique, ou[].forEach.call
- Pour être concis et lisible, en ciblant uniquement les navigateurs evergreen (et en misant sur la mise à jour de Edge) :
Bouclefor..of
NodeList
C’est une autre structure ressemblant à un tableau JavaScript, sans un être un.
NodeList est retournée par les méthodes de Browser API querySelectorAll et childNodes
Iteration de NodeList
avec forEach
Bien que n’étant pas un véritable Array
, une NodeList
peut être itérée directement avec la méthode forEach
, avec les navigateurs récents.
Avec le code HTML suivant :
<div class="element">Element 1</div>
<div class="element">Element 2</div>
<div class="element">Element 3</div>
on obtient la NodeList
et on la parcourt ainsi :
const nodeList = document.querySelectorAll('.element');
nodeList.forEach(element => console.log(element));
forEach
sur NodeList
(même pas IE 11).Il faut alors passer par un appel sur le prototype
de Array
:
Array.prototype.forEach.call(nodeList, function(element) {
console.log(element);
});
ou variante un peu plus concise :
[].forEach.call(nodeList, function(element) {
console.log(element);
});
ou passer par une boucle for
classique.
Méthodes plus adaptées
Le but de forEach
est uniquement d’obtenir chaque élément de la collection, pas de créer une nouvelle collection résultant d’une transformation, car pour cela il existe des méthodes comme filter
, map
, etc…
forEach
ou méthode filter
?
Constituer un autre tableau à partir de forEach
peut être tentant :
const toFilter = [1, 5, 10, 20];
const even = [];
function filterEven(element) {
const isEven = element % 2 === 0;
if (isEven) {
even.push(element);
}
}
// DON'T DO THAT!
toFilter.forEach(filterEven);
Mais il existe une méthode bien mieux adaptée, filter
:
const toFilter = [1, 5, 10, 20];
function filterEven(element) {
const isEven = element % 2 === 0;
return isEven;
}
const even = toFilter.filter(filterEven);
console.log(even);
Avantages
Testable
L’implémentation du callback ne repose pas sur donné extérieure (le tableau destination), ce qui le rend plus facilement testable.
Lisible
L’appel à filter
indique tout de suite l’intention de filtrer pour créer et remplir un tableau.
Assez compatible
Fonctionne avec Internet Explorer 11.
forEach
ou méthode map
?
De même la tentation peut être grande de transformer les éléments avec forEach
, et de les insérer dans un nouveau tableau, mais il existe la méthode map
spécialement conçue pour ça :
const raw = [1, 5, 10, 20];
function toObject(element) {
return { points: element };
}
const objects = raw.map(toObject);
console.log(objects);
Ce qui produit un nouveau tableau, contenant maintenant des objets :
Avantages
Les mêmes que pour filter
, plus la capacité à utiliser les promises et async / await.
De plus, les méthodes telles que filter
et map
sont chaînables et adaptée à la programmation fonctionnelle. Alors que malgré les apparences, forEach
ne l’est pas, et reste orientée programmation impérative.
Chainer plusieurs méthodes comme filter
et map
permet la « séparation des taches » : filtrer et mapper sont deux taches bien différentes. Elles n’ont pas lieu d’être mêlées dans une seule fonction, comme dans le cas d’un forEach
.
Par contre filter
, map
doivent être utilisées uniquement pour produire un résultat.
Et non pas pour causer des modifications sur l’environnement, alors que forEach
le peut.
Boucles alternatives
forEach ou for..of ?
for..of
est une nouvelle boucle apparue en JavaScript ES6, assez élégante :
for (const el of [1, 10, 20]) {
console.log(el);
}
Et il faut bien constater que forEach
ne présente pas d’avantage majeur par rapport à for-of
.
On peut mentionner quand même quelques point positifs :
Compatibilité
forEach
, apparue avec JavaScript ES5, est compatible avec Internet Explorer 11.
Index
Léger avantage pour l’accès direct à l’index, avec forEach
l’index arrive directement dans le second paramètre du callack.
Alors qu’avec for..of
, il faut itérer sur array.entries()
ou .keys()
:
for (const index of [1, 10, 20].keys()) {
console.log(index);
}
for (const [index, value] of [1, 10, 20].entries()) {
console.log(index + ': ' + value);
}
Et si lors de l’écriture du code, on avait pas prévu de gérer les index, et que le callback est une fonction séparée, pour pouvoir utiliser les index il faut aussi refactoriser l’en-tête de boucle :
Avant utilisation des index
for (const element of [1, 10, 20]) {
process(element);
}
function process(element) {
// ...
}
Après utilisation des index
for (const [key, element] of [1, 10, 20].entries() {
process(element, key);
}
function process(element, key) {
// ...
// now we can use key
}
Alors qu’avec forEach
, c’est très simple :
Avant utilisation des index
[1, 10, 20].forEach(process);
function process(element) {
// ...
}
Après utilisation des index
[1, 10, 20].forEach(process); // not touched!
function process(element, key) {
// ...
// just use key!
console.log(key + ': '+ element);
}
Elégance
Bien que for..of
soit déjà très lisible et court, forEach
est peut être encore légèrement mieux ?
Comparer :
for (const e of [1, 10, 20]) {
console.log(e);
}
et :
[1, 10, 20].forEach(e => console.log(e));
forEach
ou for
classique ?
La bonne vieille boucle for
fonctionnera dans tous les cas. Au prix d’une syntaxe un peu lourde :
const array = [1, 10, 20];
for (let i=0; i<array.length; i++) {
console.log(array[i]);
}
forEach
ou for
?
Lisibilité, fiabilité
forEach
est beaucoup plus lisible :- moins verbeux
- pas de variable temporaire
- Pas de risque de typo sur la condition de boucle (avec
for
, si on se trompe sur l’index de départ, l’infériorité stricte ou non-stricte… on tombe dans l’erreur courante « off-by-one »).
Comparer :
const a = [1, 10, 20];
for (let i=0; i<a.length; i++) {
console.log(a[i]);
}
et :
const a = [1, 10, 20];
a.forEach(e => console.log(e));
Performance
Oui, sur le papier, la performance de forEach
est inférieure à celle de for
, mais cela ne fera pas de différence dans la plupart des cas : utiliser systématiquement un for
classique serait de l’optimisation prématurée.
Appel d’une fonction avec callback
Dans le cas où l’on doit appeler une fonction tierce prenant un callback en argument, forEach
fonctionne facilement :
const a = [1, 10, 20];
a.forEach(function(element) {
// simulate asynchronous action with an element
setTimeout(function() {
console.log(element);
}, 3000);
});
Alors qu’une boucle for
, si elle gère sa variable avec var
, ne peut pas fonctionner :
for (var i = 0; i < a.length; i++) {
setTimeout(function() {
console.log(a[i]);
// does not work
}, 3000);
}
Ci-dessus, l’index i
est déjà à 3 quand le 1er accès a[i]
se fait.
Cela oblige à utiliser let
à la place de var
, donc faire une croix sur la compatibilité IE 11 (ou à utiliser encore d’autres subterfuges).
for..in
Disons le tout net, la boucle for..in
est conçue pour itérer sur les propriétés d’un objet (ou les caractères d’une string). Et non pas sur les valeurs d’un tableau (c’est tout à fait officiel, voir MDN).
for..in
va renvoyer non seulement chaque élément du tableau, mais aussi chaque propriété énumérable, ainsi que toute propriété héritée.De plus, l’ordre des itérations n’est pas garanti.Donc pour un tableau, on oublie for..in
!
forEach
sur un objet JavaScript, notamment JSON, en passant par Object.keys
. Voir un exemple.Conclusion : forEach
et les autres boucles
Nous avons vu les forces et faiblesses de forEach
, for..of
et for
, qui sont toutes des alternatives utilisables pour itérer sur un tableau. Par contre for..in
est à éviter sur un tableau.
Autres exemples pour la route
Détecter le dernier élément
Pour tester si un élément est le dernier du tableau, il suffit de passer l’index et le tableau en arguments au callback :
const array =['1st element', '2nd element', 'last element'];
array.forEach((element, index, currentArray) => {
if (index === currentArray.length - 1) {
console.log(element);
}
});
forEach
sur une table
HTML
Pour retrouver les cellules de cette table
:
<table class="iterate-me">
<tr>
<th>col 1</th>
<th>col 2</th>
</tr>
<tr>
<td>cell 1</td>
<td>cell 2</td>
</tr>
<tr>
<td>cell 3</td>
<td>cell 4</td>
</tr>
</table>
Une possibilité avec forEach
est de parcourir chaque tr
dans la table
, puis chaque td
dans un tr
:
const table = document.querySelector('.iterate-me');
const rows = table.querySelectorAll('tr');
rows.forEach(row => {
const cells = row.querySelectorAll('td');
cells.forEach(cell => console.log(cell));
});
Questions fréquentes
Comment…
- Comment faire un
break
- Il n’existe pas d’instruction
break
dansforEach
, par contre il y a d’autres solutions - Comment faire un
continue
- Il suffit de remplacer le
continue
par unreturn
- Comment obtenir l’index
- Le 2ème argument du callback permet d’accéder à l’index
- Ajouter à un
Array
- En fait, ce n’est pas la vocation de forEach de produire une autre collection. Pour cela, il existe les méthodes comme
map
etfilter
- Array vers JSON
- Plutôt qu’utiliser
forEach
, il y aJSON.stringify
qui permet de convertir le tableau en unestring
JSON :const array = ['High', 5]; let json = JSON.stringify(array); console.log(`Produced type ${typeof json}, and value ${json}`);
Produced type string, and value ["High",5]
Erreurs
forEach is not a function
C’est ce qui arrive quand on applique la méthode forEach
à un objet ressemblant à un Array
, mais qui n’en est pas un, et qui ne supporte pas forEach
. Comme par ex. HTMLCollection
. Voir plus haut comment parcourir cette structure.
undefined is not a function
L’argument passé doit être une référence de fonction. Et non une invocation de fonction comme ci-dessous :
const array = [4, 5];
array.forEach(someFunction());
function someFunction() {
// do whatever
console.log('Found an element');
}
Donc, pas de parenthèses après la fonction en argument :
const array = [4, 5];
array.forEach(someFunction);
function someFunction() {
// do whatever
console.log('Found an element');
}

+ en bonus : le Guide Facile des Boucles
Et maintenant, allez-vous utiliser forEach, ou plutôt une autre boucle ? Partagez votre avis dans les commentaires !