Map.prototype.forEach()

Runs a callback once for every entry, in insertion order, passing value before key.

Signature
forEach(callbackFn)
forEach(callbackFn, thisArg)
  • Added in JavaScript ES2015

Parameters

ParameterTypeDescription
$callbackFnFunctionA 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).
$thisArgoptionalany= undefinedA 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

Try it yourself
Loading editor…
Press Run to execute the code.

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.

Example · javascript
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.

Example · javascript
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

  • Be the first to comment on this lesson.