Difference between map and object in JavaScript
Object: string/symbol keys, prototype chain (collisions like `__proto__`), no insertion order guarantee for integer-like keys, JSON-friendly. Map: any key (including objects, NaN, null), preserves insertion order, has `.size`, no prototype chain. Use Map when keys are non-strings, you need size, or iteration order matters; object for JSON-shaped data.
Key differences
| Object | Map | |
|---|---|---|
| Key types | String, Symbol | Any (objects, NaN, null, primitives) |
| Prototype chain | Yes (collision hazards) | No |
| Insertion order | Mostly preserved; integer-like keys sort | Strictly preserved |
| Size | Object.keys(o).length (O(n)) | map.size (O(1)) |
| Iteration | for..in, Object.keys/entries | for..of, .entries(), .forEach |
| Spec performance | Optimized for fixed shapes | Optimized for frequent add/remove |
| JSON | Native (JSON.stringify) | Custom serialization needed |
Object pitfalls
const o = {};
o["__proto__"] = "x"; // doesn't set a string key; touches prototype
o.constructor = "y"; // shadow built-in propertyMitigate with Object.create(null):
const safe = Object.create(null);
safe.__proto__ = "x"; // now just a regular string keyMap advantages
const m = new Map();
m.set({ id: 1 }, "alice");
m.set(NaN, "weird");
m.size; // 2
m.get(NaN); // "weird" (Map treats NaN consistently)- Object keys store user-by-object: no stringification.
- Iteration order is insertion order, strictly.
sizeis O(1).- Frequent add/remove is faster than
delete obj[k](which can deoptimize shape).
When to use which
| Need | Use |
|---|---|
| JSON-friendly data | Object |
| Small fixed shape (3-5 known keys) | Object |
| Configurable / dynamic keys, especially non-string | Map |
| Frequent add/remove | Map |
| Need iteration in insertion order | Map |
Need .size | Map |
| Cache keyed on objects (no GC) | Map; WeakMap if you want GC |
WeakMap
Like Map but keys are objects only, weakly held. When the key object is GC'd, the entry vanishes — great for "private state per object" patterns and memoization keyed on object identity.
Performance reality
For small fixed-shape objects (records), object literals are faster than Maps thanks to V8's hidden-class optimization. For dynamic, keyed-by-anything collections that change a lot, Map wins.
Serialization
const m = new Map([[1, "a"], [2, "b"]]);
JSON.stringify(m); // "{}" — doesn't work
JSON.stringify([...m]); // "[[1,"a"],[2,"b"]]"
JSON.stringify(Object.fromEntries(m)); // "{\"1\":\"a\",\"2\":\"b\"}"Map needs help to serialize.
Iteration
for (const [k, v] of m) ...
m.forEach((v, k) => ...);Plus m.keys(), m.values(), m.entries().
Interview framing
"Object: string/symbol keys, prototype chain (collision hazards like __proto__), good for fixed-shape data and JSON. Map: any keys including objects, no prototype chain, preserves insertion order strictly, has O(1) .size, and is faster for frequent add/remove. Use Object for records and JSON. Use Map when keys can be objects, you need ordered iteration, you'll add/remove a lot, or you need .size. WeakMap for object-keyed entries that should die with the key. Object.create(null) if you want object semantics but no prototype chain."
Follow-up questions
- •Why is Object.create(null) safer than {}?
- •When is WeakMap the right choice?
- •How would you serialize a Map?
Common mistakes
- •Object keys colliding with prototype methods.
- •Trusting Object key order with integer-like keys.
- •Trying to use objects as object-keys (they all stringify to '[object Object]').
Performance considerations
- •Object literals optimize for stable shape; Map for dynamic shape. Pick by shape, not just feature.
Edge cases
- •NaN as Map key works; in object stringifies.
- •Integer-like string keys ordered numerically in objects.
- •WeakMap can't iterate.
Real-world examples
- •Caches (Map), records (Object), private state per instance (WeakMap), Redux state (Object).