Map in JavaScript: When an Object Stops Being the Right Tool
A reflection on when JavaScript objects stop being the right tool, and how Map provides a more precise way to represent relationships between keys and values.
For a long time, JavaScript developers used objects for almost everything.
Need to store data? Use an object.
Need a lookup table? Use an object.
Need a dictionary? Still an object.
It works â until it doesnât.
At some point, you run into behavior that feels slightly off. Keys donât behave the way you expect. Iteration feels awkward. Or worse, things silently break.
Thatâs usually the moment when you realize: maybe this isnât an âobject problemâ anymore.
Itâs a Map problem.
Objects Were Never Just Key-Value Stores
Objects look like key-value storage:
const user = {
name: "Rishi",
age: 24
}But they are not designed purely for that purpose.
An object is:
data + behavior + prototype chainThat means:
- keys are limited to strings (or symbols)
- keys can collide with built-in properties
- thereâs implicit behavior coming from prototypes
For simple data modeling, this is fine.
But when you start using objects as dynamic storage, these details begin to matter.
The First Crack: Keys That Arenât Strings
Consider this:
const obj = {}
const key = { id: 1 }
obj[key] = "data"You might expect the object itself to be used as the key.
But what actually happens is:
key â "[object Object]"The identity of the key is lost.
Two different objects become the same key.
This is not a bug. Itâs how objects work.
And itâs often the first sign that youâre using the wrong structure.
Map: A Structure Designed for Mapping
Map exists to do one thing well:
store relationships between keys and valuesconst map = new Map()
const key = { id: 1 }
map.set(key, "data")
map.get(key) // "data"No string conversion.
No collisions.
The key remains exactly what it is.
This is the core difference:
Objects transform keys.
Maps preserve them.
When Objects Start Feeling Awkward
There are patterns where objects start to feel like youâre forcing them into something they werenât designed for.
Counting values:
counts[userId] = (counts[userId] || 0) + 1Dynamic insertions and deletions:
obj[key] = value
delete obj[key]Iteration:
for (const key in obj) {
console.log(key, obj[key])
}None of these are wrong.
But they are slightly awkward.
They rely on conventions rather than intention.
Map Makes the Intent Explicit
With Map, the same patterns become more direct:
map.set(userId, (map.get(userId) || 0) + 1)map.delete(key)for (const [key, value] of map) {
console.log(key, value)
}The API is designed for exactly these operations.
Thereâs no mixing with prototype behavior. No ambiguity about whatâs happening.
The Hidden Cost of Using Objects as Maps
The biggest issue is not syntax â itâs semantics.
When you use an object as a map, you are mixing two different roles:
object as a model
object as a data structureFor example:
const user = {
name: "Rishi"
}This is a representation of a real-world entity.
But:
const cache = {}This is not a âthingâ.
Itâs a relationship store.
Using the same structure for both can blur intent and introduce subtle bugs.
Where Map Really Shines
There are certain situations where Map is clearly the right tool.
When keys are not strings:
map.set(obj, value)When identity matters:
map.get(sameObjectReference)When you frequently add and remove entries:
map.set(...)
map.delete(...)When iteration is central to your logic:
for (const [k, v] of map)When youâre building:
- caches
- lookup tables
- registries
- frequency counters
Where Object Is Still Better
Objects are still the right choice when youâre modeling structured data.
const user = {
name: "Rishi",
age: 24
}This is not a mapping problem.
Itâs a representation of a concept.
Objects are also easier when working with JSON, since Map doesnât serialize directly.
A Pattern You Start to Notice
As JavaScript evolved, a pattern emerged.
Instead of changing existing behavior, the language introduces new tools alongside it.
letandconstinstead of modifyingvarclassinstead of replacing prototypesBigIntinstead of fixingnumberMapinstead of redefining objects
Map is part of that evolution.
It doesnât replace objects.
It exists because objects couldnât safely handle every use case.
A Better Way to Think About It
Itâs tempting to think:
âMap is a better object.â
But thatâs not quite right.
A more useful way to see it is:
Objects represent things.
Maps represent relationships.
Once you start thinking in those terms, the choice becomes clearer.
And many small design decisions in your code start to feel more intentional.