Wenn du Arrays in JavaScript iterieren willst, bietet dir Array.prototype.forEach eine klare, gut lesbare und funktionale Möglichkeit, für jedes Element Nebenwirkungen auszuführen. In diesem Artikel zeige ich dir, wie du javascript foreach korrekt einsetzt, wann du besser andere Iterationsformen verwendest und welche Details in der Praxis wirklich zählen – inklusive Codebeispielen, Vergleichstabellen, Performance-Hinweisen und konkreten Empfehlungen.
Was forEach ist – und wofür du es einsetzt
forEach führt eine Callback-Funktion für jedes vorhandene Element in einem Array aus, in aufsteigender Index-Reihenfolge. Die Methode gibt immer undefined zurück und ist damit ideal für Nebenwirkungen (z. B. Logging, DOM-Manipulation, mutierendes Updaten externer Strukturen). Sie ist weniger geeignet für Transformationen, denn sie erzeugt kein neues Array – dafür sind map oder filter zuständig.
Kernaussage: Nutze
forEachfür Nebenwirkungen pro Element. Nutzemap/filter/reducefür Transformationen und Aggregation.
Syntax und Parameter
array.forEach(callback[, thisArg])
Die Callback-Funktion erhält bis zu drei Parameter: den aktuellen Wert, den Index und eine Referenz auf das gesamte Array. Optional kannst du den this-Kontext über thisArg setzen (gilt nur bei traditionellen Funktionen, nicht bei Pfeilfunktionen).
Eine präzise Indizierung der Elemente unterstützt dich dabei, deren Position im Array eindeutig zu identifizieren.
| Parameter | Typ | Beschreibung |
|---|---|---|
callback |
Function | Wird für jedes vorhandene Element aufgerufen. |
currentValue |
T | Aktueller Elementwert. |
index |
number | Nullbasierter Index des Elements. |
array |
T[] | Referenz auf das Array, auf dem forEach aufgerufen wurde. |
thisArg |
any (optional) | Wert für this innerhalb des Callbacks (nicht bei Pfeilfunktionen). |
Beispiel mit traditioneller Funktion und thisArg:
const nums = [1, 2, 3];
const ctx = { factor: 10 };
nums.forEach(function (n) {
console.log(n * this.factor);
}, ctx);
// Ausgabe: 10, 20, 30
Beispiel mit Pfeilfunktion (lexikalisches this, thisArg wird ignoriert):
const nums = [1, 2, 3];
const factor = 10;
nums.forEach(n => {
console.log(n * factor);
});

Praxisbeispiele – korrekt und idiomatisch
Ein einfacher Start: Logge jedes Element.
['Apfel', 'Birne', 'Banane'].forEach(frucht => console.log(frucht));
Typischer Anti-Pattern-Versuch (Transformation mit forEach):
// Bitte nicht so:
const input = [1, 2, 3];
const doubled = [];
input.forEach(n => doubled.push(n * 2));
Besser mit map:
const input = [1, 2, 3];
const doubled = input.map(n => n * 2);
DOM/NodeList: In modernen Browsern kannst du NodeLists direkt iterieren. Sehr nützlich für UI-Initialisierung.
document.querySelectorAll('button').forEach(button => {
button.addEventListener('click', () => {
// handle click
});
});
Aggregation (z. B. Summe) geht mit forEach, aber reduce ist idiomatischer:
// Mit forEach
let sum = 0;
[1, 2, 3, 4].forEach(n => { sum += n; });
// Mit reduce (empfohlen)
const sum2 = [1, 2, 3, 4].reduce((acc, n) => acc + n, 0);
Kontrollfluss: Warum du mit forEach nicht „breaken“ kannst
forEach unterstützt kein break oder continue. Ein return im Callback beendet nur die aktuelle Callback-Ausführung – es wirkt wie ein lokales „continue“, nicht wie break auf die gesamte Iteration. Wenn du frühzeitig abbrechen willst, sind folgende Alternativen sinnvoll:
for...of: Voller Kontrollfluss mitbreak/continue.some: Bricht ab, sobald der Callbacktrueliefert.every: Bricht ab, sobald der Callbackfalseliefert.find: Gibt das erste passende Element zurück (implizit frühzeitiger Abbruch).
Beispiel mit some (simuliertes break):
[1, 2, 3, 4, 5].some(n => {
if (n > 3) return true; // Abbruch
console.log(n);
return false;
});
// Ausgabe: 1, 2, 3
Mit for...of hast du volle Kontrolle:
for (const n of [1, 2, 3, 4, 5]) {
if (n > 3) break;
console.log(n);
}
Asynchronie: Warum await mit forEach nicht tut
Ein häufiger Fehler ist der Einsatz von async/await in Kombination mit forEach. Die Methode wartet nicht auf Promises; der übergeordnete Code läuft weiter.
Hierbei spielt Async/Await eine zentrale Rolle, um asynchrone Abläufe korrekt zu steuern.
Fehlannahme:
// Tut NICHT, was man erwartet
await users.forEach(async user => {
await saveUser(user); // Diese Promises werden nicht gesammelt/abgewartet
});
console.log('Alle gespeichert'); // Läuft zu früh
Korrekt sequenziell:
for (const user of users) {
await saveUser(user);
}
console.log('Alle gespeichert');
Korrekt parallel (kontrolliert, mit Fehleraggregation):
await Promise.all(users.map(user => saveUser(user)));
console.log('Alle gespeichert');
Merksatz: Nutze
for...offür sequenzielle Asynchronität. Nutzemap+Promise.allfür parallele Asynchronität. Vermeide javascript foreach mitawait.

Sparse Arrays, Löcher und Mutationen während der Iteration
forEach iteriert nur über vorhandene Einträge. „Löcher“ in Arrays (z. B. durch new Array(5) oder ausgelassene Elemente) werden übersprungen.
const arr = [1, , 3];
arr.forEach((v, i) => console.log(i, v));
// Ausgabe: "0 1" und "2 3" (Index 1 wird übersprungen)
Die Iteration verwendet die zu Beginn bestimmte Länge. Elemente, die du während der Iteration hinten hinzufügst, werden nicht mehr besucht. Änderungen an bereits existierenden Elementen wirken, wenn der Index noch nicht verarbeitet ist. Gelöschte Elemente werden übersprungen.
const arr = [1, 2, 3, 4];
arr.forEach((v, i) => {
if (i === 0) arr.push(99); // Wird NICHT verarbeitet
if (i === 1) delete arr[2]; // "3" wird übersprungen
});
Vergleich: forEach vs. for vs. for…of vs. map/reduce/some/every
| Merkmal | forEach | for | for…of | map | reduce | some/every |
|---|---|---|---|---|---|---|
| Rückgabewert | undefined | n/a | n/a | Neues Array | Aggregatwert | Boolean |
| break/continue | Nein | Ja | Ja | Nein | Nein | Abbruch nach Bedingung |
| Asynchron mit await | Nein | Ja | Ja | Mit Promise.all |
Ja (innerhalb Callback), selten sinnvoll | Eher nein |
| Hauptzweck | Nebenwirkungen | Maximale Kontrolle | Lesbar + Kontrolle | Transformation | Aggregation | Prädikate/Wachhunde |
| Iteriert Löcher | Nein | Ja (über Indizes) | Nein (über Werte) | Nein | Nein | Nein |
| Performance | Gut, aber Funktions-Overhead | Sehr gut | Gut | Gut | Gut | Gut |
Fazit aus der Tabelle: Nutze javascript foreach für klare Nebenwirkungen; wechsle zu for/for...of für Kontrolle und Asynchronie; verwende map/reduce/some/every für deklarative Datenoperationen.
Performance realistisch einschätzen
for-Schleifen sind in Microbenchmarks häufig schneller als forEach, weil sie ohne Funktionsaufruf-Overhead auskommen. In der Praxis spielt dieser Unterschied oft nur bei sehr großen Arrays oder in Performance-Hotspots eine Rolle. Bevor du optimierst, miss in deinem konkreten Kontext. Moderne Engines optimieren Iterationen aggressiv; Lesbarkeit und Korrektheit sind meist wichtiger als theoretische Nanosekunden.
- Große Datenmengen oder enge Schleifen: Eher
foroderfor...of. - Normale UI-/Backend-Logik:
forEachist vollkommen okay. - Immer: Zuerst messen, dann optimieren.
this, Pfeilfunktionen und thisArg – worauf du achten musst
Mit klassischen Funktionen kannst du thisArg setzen. Pfeilfunktionen binden this lexikalisch und ignorieren thisArg. Das ist in der Regel gewollt, weil Pfeilfunktionen das umgebende this erben – weniger Überraschungen, weniger Boilerplate.
// thisArg wirkt nur bei klassischer Funktion
items.forEach(function (item) {
this.handle(item);
}, handlerObj);
// Pfeilfunktion: this stammt aus äußerem Scope
items.forEach(item => {
handlerObj.handle(item);
});
Vorsicht: forEach gibt es auch auf Map und Set – mit anderer Signatur
Neben Arrays besitzen auch Map und Set eine forEach-Methode – allerdings mit anderer Parameterreihenfolge:
- Map.prototype.forEach(value, key, map)
- Set.prototype.forEach(value, valueAgain, set) (zweites Argument ist aus Kompatibilitätsgründen ebenfalls der Wert)
const m = new Map([['a', 1], ['b', 2]]);
m.forEach((value, key) => {
console.log(key, value);
});
const s = new Set([1, 2, 3]);
s.forEach((value) => {
console.log(value);
});
Verwechsle diese Signaturen nicht mit Array.prototype.forEach(value, index, array), sonst entstehen schwer auffindbare Bugs.
NodeList.forEach im DOM
Viele Browser bieten NodeList.prototype.forEach, sodass du Ergebnislisten von querySelectorAll wie Arrays behandeln kannst. In älteren Browsern (insbesondere IE) fehlt diese Methode. In solchen Fällen wandelst du die NodeList in ein Array um oder nutzt ein Polyfill.
// Kompatibel in modernen Umgebungen:
document.querySelectorAll('.item').forEach(el => el.classList.add('active'));
// Fallback: Array.from
Array.from(document.querySelectorAll('.item'))
.forEach(el => el.classList.add('active'));
Fehlerbehandlung: Sync vs. Async
Synchron: Nutze einfach try...catch im Callback, wenn einzelne Iterationen fehlschlagen können, ohne die ganze Verarbeitung abzubrechen.
orders.forEach(order => {
try {
processOrder(order);
} catch (e) {
console.error('Fehler bei Order', order.id, e);
}
});
Asynchron: try...catch fängt innerhalb eines async-Callbacks zwar den lokalen Fehler, aber forEach wartet nicht. Entweder nutzt du for...of mit await und try...catch pro Iteration, oder du erstellst ein Promise-Array und sammelst Fehler mit Promise.allSettled.
// Robust parallel mit Fehleraggregation
const results = await Promise.allSettled(users.map(u => saveUser(u)));
const failed = results.filter(r => r.status === 'rejected');
if (failed.length) {
console.warn('Fehlerhafte Saves:', failed.length);
}
TypeScript: Signatur und Typinferenz
In TypeScript lautet die relevante Signatur:
forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void
Das heißt: Typen deiner Array-Elemente (T) werden in value korrekt abgeleitet. forEach gibt void zurück; du kannst also nicht auf ein Ergebnis zugreifen, außer du schreibst in externe Variablen oder Rufzustände. Für Transformationen nimmst du map, für Aggregation reduce. Beispiel:
type User = { id: string; name: string };
const users: User[] = [{ id: '1', name: 'Ada' }];
users.forEach(u => {
// u: User
console.log(u.id, u.name);
});
Best Practices – komprimiert
- Nutze forEach für Nebenwirkungen (Logging, DOM-Manipulation, mutierende Operationen auf externen Strukturen).
- Benutze map/filter/reduce für Transformationen und Aggregationen.
- Für frühen Abbruch oder await nimm for…of (oder
some/every/findje nach Zweck). - Vermeide asynchrone Callbacks in
forEach; nutze stattdessenPromise.alloderfor...of. - Erwarte kein Ergebnis von
forEach; Rückgabewert istundefined. - Beachte Sparse Arrays: Löcher werden übersprungen.
- In Hot Paths und bei sehr großen Arrays ggf.
for/for...ofnutzen. - Nutze Pfeilfunktionen für klareres
this-Verhalten;thisArgnur bei Bedarf. - Unterscheide Array-/Map-/Set-forEach und deren Callback-Signaturen.
Kompatibilität und Polyfills
Array.prototype.forEach ist seit ES5 breit unterstützt (moderne Browser, aktuelle Node.js-Versionen). In sehr alten Browsern (z. B. IE <= 8) ist forEach nicht vorhanden; in IE 9 fehlt NodeList.forEach. Falls du solche Umgebungen unterstützen musst, nutze Polyfills oder Fallbacks.
Minimaler Polyfill (nur als Beispiel; in produktiven Umgebungen besser geprüfte Polyfills verwenden):
if (!Array.prototype.forEach) {
Array.prototype.forEach = function (callback, thisArg) {
if (this == null) throw new TypeError('this is null or not defined');
const O = Object(this);
const len = O.length >>> 0;
if (typeof callback !== 'function') throw new TypeError(callback + ' is not a function');
let k = 0;
while (k < len) {
if (k in O) callback.call(thisArg, O[k], k, O);
k++;
}
};
}
Häufige Stolperfallen zusammengefasst
- Kein break/continue: Nutze
for...ofodersome/every. - Kein await:
forEachignoriert Promises; nimmfor...ofoderPromise.all. - Kein neues Array: Für Transformationen ist
mapdie richtige Wahl. - Sparse Arrays: Löcher werden ausgelassen.
- Mutationen: Die anfängliche Länge ist fix; spätere Anhänge werden nicht besucht.
- Map/Set: Andere Callback-Signaturen als bei Arrays.
Konkrete Muster aus der Praxis
1) Objektindizes validieren und protokollieren
const users = [{ id: '1' }, { id: '2' }, null, { id: '4' }];
users.forEach((u, i) => {
if (!u) {
console.warn('User fehlt bei Index', i);
return; // entspricht lokalem "continue"
}
console.log('User-ID:', u.id);
});
2) UI initialisieren
document.querySelectorAll('[data-tooltip]').forEach(el => {
const text = el.getAttribute('data-tooltip') || '';
el.addEventListener('mouseenter', () => showTooltip(el, text));
el.addEventListener('mouseleave', () => hideTooltip(el));
});
3) Fehler robust abfangen (sync)
configs.forEach(cfg => {
try {
applyConfig(cfg);
} catch (e) {
reportConfigError(cfg, e);
}
});
4) Nebenwirkungen bewusst kapseln
const events = getEvents();
const logger = makeEventLogger({ level: 'info' });
events.forEach(e => logger.log(formatEvent(e)));
Wann du bewusst nicht forEach nimmst
- Du brauchst ein Ergebnis-Array:
map. - Du willst filtern:
filter. - Du berechnest einen Aggregatwert (Summe, Max, Index-Map):
reduce. - Du willst früh abbrechen:
for...ofodersome/every/find. - Du brauchst await innerhalb der Iteration:
for...of. - Du willst Löcher explizit verarbeiten: klassische
for-Schleife über Indizes.
Fazit
javascript foreach ist ein starkes Werkzeug, wenn du Nebenwirkungen pro Element ausführen willst, ohne ein Ergebnis zu erzeugen. Es glänzt durch Lesbarkeit, einfache Syntax und klaren Fokus. Seine Grenzen sind ebenso wichtig: kein break/continue, kein await, keine neuen Arrays. Für Transformationen sind map/filter/reduce die idiomatischen Alternativen; für kontrollierte oder asynchrone Iteration ist for...of meist die bessere Wahl. Wenn du diese Rollen sauber trennst, bekommst du wartbaren, performanten und robusten Code.
FAQ
Was ist der Unterschied zwischen forEach und map?
forEach führt Nebenwirkungen aus und gibt undefined zurück. map transformiert Elemente und gibt ein neues Array zurück. Für Transformationen ist map die richtige Wahl.
Wie breche ich eine forEach-Schleife ab?
Direkt gar nicht. Nutze for...of mit break, oder setze je nach Anwendungsfall some (Abbruch bei true), every (Abbruch bei false) oder find (liefert erstes Treffer-Element).
Kann ich await in forEach verwenden?
Technisch ja, praktisch nein – forEach wartet nicht auf Promises. Für sequenzielle Asynchronie: for...of. Für parallele: Promise.all(users.map(fn)).
Warum überspringt forEach „Löcher“ in Arrays?
Weil es nur über tatsächlich vorhandene Einträge iteriert. Indizes ohne Wert (Sparse Arrays) werden nicht besucht. Wenn du alle Indizes brauchst, nutze eine klassische for-Schleife.
Ist forEach langsamer als eine for-Schleife?
Oft ja, wegen Funktionsaufrufen und weniger Optimierungspotenzial. In der Praxis ist der Unterschied meist irrelevant – außer in Hot Paths oder bei sehr großen Arrays. Miss in deinem Kontext, bevor du optimierst.
Was ist der Unterschied zwischen Array.forEach und Map/Set.forEach?
Bei Arrays lautet die Signatur (value, index, array). Bei Map ist sie (value, key, map); bei Set (value, value, set). Verwechsle diese nicht.
Kann ich mit forEach ein neues Array aufbauen?
Nicht direkt. Du kannst zwar in ein externes Array pushen, aber idiomatisch und sicherer ist map oder reduce.
Wie gehe ich mit Fehlern in forEach um?
Synchron: try...catch im Callback. Asynchron: Entweder for...of mit await und try...catch, oder Promises sammeln und mit Promise.allSettled auswerten.
Unterstützen alle Browser NodeList.forEach?
Moderne Browser ja. In älteren (insbesondere IE) fehlt es. Nutze Array.from(nodeList) oder ein Polyfill als Fallback.
Kann ich während forEach das Array verändern?
Ja, aber mit Vorsicht. Die Länge wird zu Beginn bestimmt; neu angehängte Elemente werden nicht mehr besucht. Gelöschte Elemente werden übersprungen. Besser: Mutationen vermeiden oder bewusst kontrollieren.
Wann ist forEach die beste Wahl?
Wenn du pro Element klar definierte Nebenwirkungen brauchst – Logging, DOM-Event-Setup, Service-Aufrufe mit paralleler Koordination via Promise.all, oder strukturierte Verarbeitung ohne Rückgabearray. Für alles andere gibt es bessere, semantisch passendere Alternativen.