JavaScript Map vs Object: Which One Should You Use?
Both Map and ordinary objects can associate keys with values, but they behave differently in ways that matter for iteration, key choice, serialization, and property access. This guide compares those differences and shows how to choose a structure for common JavaScript tasks.
Key Takeaways
- Use
Mapwhen keys may be values other than strings or symbols, when you need a directsize, or when you frequently add and remove entries. - Use an object for records with a known set of named fields, especially when the data is naturally represented as JSON.
- A
Mapiterates in insertion order. Object key order follows specific rules, including putting array-index keys first in ascending order. - Neither structure is universally faster. Choose based on semantics and measure performance in your own workload if it matters.
The Core Difference: A Collection or a Record?
A Map is a collection of key-value entries. Its keys can be any JavaScript values, including objects, functions, and null. An ordinary object is usually a record: its properties are named with strings or symbols, and it also inherits behavior from its prototype unless created without one.
const settings = {
theme: "dark",
language: "en"
};
const scores = new Map([
["Mina", 42],
["Ravi", 37]
]);
The object reads naturally as a record with named fields. The Map reads naturally as a collection that may grow or shrink.
Key Types and Property Behavior
Object property keys are strings or symbols. When you use another value as a property key, JavaScript converts it to a property key—typically a string. For example, object[1] and object["1"] refer to the same property.
A Map keeps keys as values. Object keys are matched by identity, so two different objects with the same contents are still different keys. Map key comparison uses SameValueZero; for example, NaN can be used as a key and matches itself for lookup.
const object = {};
object[1] = "number key";
console.log(object["1"]); // "number key"
const firstUser = { id: 7 };
const secondUser = { id: 7 };
const cache = new Map();
cache.set(firstUser, "cached result");
console.log(cache.get(firstUser)); // "cached result"
console.log(cache.get(secondUser)); // undefined
The last lookup misses because secondUser is a different object, even though its contents resemble firstUser.
Ordinary objects also have property behavior beyond storing values: they can inherit properties, and properties can be enumerable, writable, or configurable. That matters when checking for keys or iterating. For example, "toString" in object can be true because of inheritance, even if the object has no own toString property. Use Object.hasOwn(object, key) when you want to check for an own property.
For a string-keyed dictionary that should not inherit from Object.prototype, you can use Object.create(null). A Map is often a more direct fit for a mutable key-value dictionary, but neither choice removes the need to validate untrusted input or control how keys are used.
Iteration and Insertion Order
A Map is iterable by default. Its default iterator yields [key, value] pairs in insertion order. Updating an existing key does not move it to the end; deleting a key and adding it again does.
Ordinary objects are not iterable by default. Use methods such as Object.entries() to iterate over enumerable own string-keyed properties. Their order is not simply insertion order: array-index property keys are listed first in ascending numeric order, followed by other string keys in creation order, then symbol keys in creation order for APIs that include symbols.
const map = new Map([
["first", 1],
["second", 2]
]);
map.set("first", 3);
console.log([...map.keys()]); // ["first", "second"]
const record = {};
record["second"] = 2;
record["10"] = 10;
record["2"] = 2;
console.log(Object.keys(record)); // ["2", "10", "second"]
Use Map when you need predictable collection iteration in the order entries were added. Use objects for records where named properties matter more than treating all fields as entries in a collection.
Size and Common Operations
A Map exposes its entry count through the size property. Objects have no built-in property count. Object.keys(record).length counts enumerable own string keys, but it does not count symbol keys or non-enumerable properties.
const map = new Map([
["active", true],
["retries", 3]
]);
console.log(map.size); // 2
const record = { active: true, retries: 3 };
console.log(Object.keys(record).length); // 2
Their APIs also reflect their different roles. A Map provides methods such as set, get, has, and delete. Objects use property access and object operations such as Object.hasOwn, Object.entries, and delete. Prefer Map.prototype.has() to determine whether a map contains a key; a stored value may itself be undefined.
Serialization and JSON
JSON.stringify() does not serialize a Map's entries by default. A map typically becomes {}. Ordinary objects serialize their enumerable own string-keyed properties, which is why they are a natural fit for many JSON records.
If map keys and values should be represented as entries, serialize them as an array and reconstruct the map from that array:
const original = new Map([
["theme", "dark"],
["fontSize", 16]
]);
const json = JSON.stringify([...original]);
const restored = new Map(JSON.parse(json));
console.log(json); // [["theme","dark"],["fontSize",16]]
console.log(restored.get("theme")); // "dark"
This works for these string keys and JSON-compatible values. JSON does not preserve every JavaScript value or key type: for example, object keys used in a Map cannot be faithfully reconstructed as the same object identities from this representation. Choose and document an explicit encoding if the map contains values that JSON cannot represent directly.
For a map whose keys are all appropriate string property names, Object.fromEntries(map) can produce an object for JSON serialization. That conversion changes the data model: object keys are property names, and the result no longer has Map methods or non-string key identity.
Which One Should You Use?
| Situation | Better starting point | Why |
|---|---|---|
| A record with known fields, such as a user profile | Object | Named properties are direct and work naturally with JSON. |
| A cache keyed by object instances | Map | Object-valued keys retain identity instead of being converted to property names. |
| A collection with frequent additions and removals | Map | Its API is designed around entry management and exposes size. |
| Configuration or data that is read and written as JSON | Object | JSON represents object properties directly. |
| A string-keyed dictionary that should not inherit object properties | Map or Object.create(null) | Both can avoid ordinary prototype inheritance; choose based on the API and serialization needs. |
Do not choose based on a blanket assumption that one is faster. Performance depends on the JavaScript engine, workload, key distribution, and operations. If performance is important, benchmark a representative workload in the runtime and environment you actually use, and make sure the benchmark measures the operation your application performs.
Common Mistakes
- Using an object when key identity matters. Object keys are converted to property keys; use a
Mapif distinct object instances must remain distinct keys. - Assuming every object key is included by
Object.keys(). It returns enumerable own string keys, not symbols, inherited properties, or non-enumerable properties. - Assuming objects iterate like maps. Array-index keys receive special ordering, so an object is not a general-purpose insertion-ordered entry collection.
- Expecting
JSON.stringify(map)to preserve entries. Convert entries explicitly and consider whether the keys and values can be represented and restored. - Treating a
Mapas a substitute for a record. A map may be less convenient when consumers expect named properties or ordinary JSON objects.
Best Practices
- Start with an object for a fixed-shape record and with a
Mapfor a collection of dynamic entries. - Use
Object.hasOwn()when you need to test for an object's own property rather than an inherited one. - Use
Map.prototype.has()for map membership instead of relying on the value returned byget(). - Decide how a map will be serialized before storing it in a format that expects JSON.
- Benchmark only when there is a measured performance concern; do not infer a universal speed advantage from the data structure alone.
Conclusion
Choose an object when the data represents named fields, and choose a Map when it represents a collection whose keys, membership, size, or iteration order need map semantics. The best choice follows from how the data should behave—not from a claim that one structure always wins.
References
- MDN: Map — map keys, methods, iteration, and size.
- MDN: Object — object properties and object methods.
- ECMAScript specification: Keyed Collections — specification for
Mapand other keyed collections. - ECMAScript specification: OrdinaryOwnPropertyKeys — property-key ordering for ordinary objects.
