Map.prototype.keys, values, entries and forEach

All four traverse entries in insertion order, guaranteed, whatever the key types. That guarantee is what an object cannot give you: integer-like keys always jump to the front there.

Map methodsES2015Live demo
Common call
for (const [k, v] of map)
Returns
iterators; forEach returns undefined
Replaces
Object.entries with its reordering
Watch out
forEach passes (value, key) — the reverse of what you expect
map.keys(), map.values(), map.entries(), map.forEach(cb)
→ Iterator

Demo

Live evaluation
Try:
Inputs
jsonstringJSON pairs with mixed key types
Output
(m => [[...m.keys()], [...m.values()]])(new Map(JSON.parse('[["b",1],[2,2],["a",3]]')))
[['b', 2, 'a'], [1, 2, 3]]

The first case is the whole argument for Map. The keys were inserted as 'b', 2, 'a' and they come back in exactly that order. Build the same data as an object and Object.keys gives ['2', 'b', 'a'] — the integer-like key is moved to the front and converted to a string. The third case makes it starker: keys 3, 1, 2 stay in that order here, where an object would sort them to 1, 2, 3. If order carries meaning, an object is the wrong container.

Parameters

NameTypeRequiredDescription
callbackFunctionno (none)For forEach only. Called as callback(value, key, map) — value first, which is the opposite order from Object.entries pairs.

Return value

Iterator — The first three return iterators, not arrays — spread them or loop them. forEach returns undefined and calls back with (value, key, map) — value FIRST.

Common patterns

Iterate entries
A Map is directly iterable — entries() is implicit.
for (const [key, value] of map) { }
Values or keys alone
Spread into an array to use array methods.
const total = [...map.values()].reduce((a, b) => a + b, 0);
forEach, watching the order
Value first, key second.
map.forEach((value, key) => console.log(key, value));

Examples

1. Insertion order kept
[...new Map([["b", 1], [2, 2], ["a", 3]]).keys()]
Returns
['b', 2, 'a']
2. An object reorders
Object.keys({b: 1, 2: 2, a: 3})
Returns
['2', 'b', 'a']
3. Direct iteration
[...new Map([["a", 1]])]
Returns
[['a', 1]]
4. entries is the default
new Map().entries === undefined
Returns
false
5. forEach is value-first
new Map([["a", 1]]).forEach((v, k) => console.log(v, k))
Returns
1 a
6. No length on an iterator
new Map().keys().length
Returns
undefined

Pitfalls

1. forEach passes value BEFORE key
The signature is (value, key, map), which is the reverse of how a Map is written and the reverse of an Object.entries pair. Code that destructures the first parameter as a key gets the value, and usually still runs — producing wrong output rather than an error.
Reversed
new Map([["a", 1]]).forEach((k, v) => `${k}=${v}`)
'1=a'
Correct order
new Map([["a", 1]]).forEach((v, k) => `${k}=${v}`)
'a=1'
2. keys, values and entries return iterators, not arrays
There is no length and no indexing. Modern runtimes DO give iterators map and filter via the ES2025 iterator helpers — but those return further iterators, not arrays, so you still need toArray or a spread to get one. Older runtimes have neither.
Still an iterator
new Map([["a", 1]]).values().map(x => x)
Object [Iterator Helper] {}
Materialise it
[...new Map([["a", 1]]).values()].map(x => x)
[1]
3. Spreading into an object loses everything
Object spread copies own enumerable PROPERTIES, and a Map keeps its entries internally — so {...map} is an empty object. Use Object.fromEntries, which consumes the iterator properly.
Empty
({...new Map([["a", 1]])})
{}
fromEntries
Object.fromEntries(new Map([["a", 1]]))
{a: 1}
4. Mutating during iteration is visible
The iterator reflects live changes — an entry added ahead of the cursor WILL be visited, which can loop forever if you add on every step. Iterate a spread snapshot when you intend to modify.
Can loop forever
for (const [k] of m) m.set(k + "x", 1);
never terminates
Snapshot
for (const [k] of [...m]) m.set(k + "x", 1);
terminates

When to use

Use it
  • Iterating a collection where insertion order matters
  • Keys that are numbers or objects, where an object would reorder or coerce
  • Feeding keys or values into array methods, via spread
  • Converting to pairs for serialisation
Reach for something else
  • You want array methods directly → spread first, or keep an array
  • Serialising → Object.fromEntries or spread to pairs
  • Order does not matter and keys are strings → an object is simpler
  • You need random access by index → an array

Notes

Complexity
O(n) to traverse; O(1) to obtain an iterator
Return
Iterators that yield in insertion order; forEach returns undefined
CPython impl
V8: Builtins-map-iterators / OrderedHashMap
Memory
Iterators are lazy — spreading materialises an array
Thread-safe
Single-threaded; iterators observe concurrent mutation

FAQ

No — a Map is iterable and its default iterator IS entries, so for...of over the Map itself yields [key, value] pairs. Writing entries() explicitly is harmless and occasionally clearer.

for (const [k, v] of map) { }
for (const [k, v] of map.entries()) { }   // identical

History

ES2015
Map added with keys, values, entries, forEach and a guaranteed insertion order.