Map.prototype.forEach()
Runs a callback once for every entry, in insertion order, passing value before key.
forEach(callbackFn)
forEach(callbackFn, thisArg)- Added in JavaScript ES2015
Parameters
| Parameter | Type | Description |
|---|---|---|
$callbackFn | Function | A function executed for each entry, called with three arguments: value (the value of the current entry), key (the key of the current entry), and map (the Map being traversed). |
$thisArgoptional | any= undefined | A value to use as this when executing callbackFn. If omitted, undefined is passed. |
Return value
undefinedundefined. The method exists for its side effects and cannot be chained.
Errors
Throws a TypeError when callbackFn is not callable, or when called on a value that is not a real Map.
Changelog
- PHP ES2015Introduced with the Map built-in.
The argument order is (value, key, map) — value first. Everyone gets this backwards at least once, because for (const [key, value] of map) puts the key first and because Array.prototype.forEach passes (element, index). Reading it as "element, then its index-equivalent" makes it consistent, but the mismatch with destructuring is a real source of bugs that never throw — you just get keys where you expected values.
When to use it — and when not to
Prefer for...of over the map in almost all cases: it destructures in the natural order, and it supports break, continue, return and await. forEach() cannot be stopped early (short of throwing), and an async callback is fired off without being awaited, so the loop finishes before the work does. It earns its place when you already have a named function to apply, or when thisArg lets you bind a method cleanly.
Mutation during the loop
Entries deleted before they are reached are skipped, and entries added during iteration are visited — so a callback that keeps adding keys can loop forever. Overwriting an existing key is safe.
Example
When to use it
- Apply an existing named function to every stored record
- Log or report every entry with its key for debugging
- Collect entries matching a condition into an array
- Bind a collector method as thisArg while walking the map
Frequently asked questions
What order are the arguments in Map.forEach?
The callback is called as (value, key, map) — the value comes first, which is the reverse of the [key, value] pairs you destructure in a for...of loop.
How do I break out of a Map.forEach loop?
You cannot. forEach always visits every remaining entry; use for...of over the map when you need break, continue, return or await.
Is it safe to modify a Map while forEach is running?
Deleting is safe — removed entries not yet reached are skipped. Adding entries is also visited in the same pass, so a callback that keeps inserting will loop forever.
More examples
Deleting during the loop is safe; adding is not
The first loop prints a and c only, because "b" was removed before the iterator reached it. The second shows newly added keys being visited in the same pass — without the counter guard it would never terminate.
const m = new Map([["a", 1], ["b", 2], ["c", 3]]);
m.forEach((value, key) => {
if (key === "a") m.delete("b"); // "b" never visited
console.log("visited", key, value);
});
const grow = new Map([["x", 1]]);
let added = 0;
grow.forEach((v, k) => {
if (added < 2) { grow.set(`${k}!`, v); added++; }
});
console.log([...grow.keys()]);Using thisArg
Passing collector as thisArg makes this inside the method refer to the object, so an unbound method reference still works. With an arrow function the second argument would be ignored, since arrows keep their surrounding this.
const collector = {
names: [],
add(value, key) {
if (value > 1) this.names.push(key);
},
};
new Map([["a", 1], ["b", 5], ["c", 9]])
.forEach(collector.add, collector);
console.log(collector.names);
console.log(collector.names.length);
Discussion