Standard Library: Collections
|
This section documents modern ECMAScript and core browser JavaScript APIs — it is not tied to any specific framework or library (React, Vue, Angular, etc.). This content was generated with the assistance of AI and should be verified against the current ECMAScript specification and MDN documentation before relying on it in production, since JavaScript language features and browser API support continue to evolve. This section’s bibliography lists the reference material consulted while preparing these pages. |
Plain objects and arrays cover most everyday data-structure needs, but JavaScript also ships four dedicated
collection classes — Set, Map, WeakSet, and WeakMap — that solve problems objects and arrays handle
poorly: fast membership testing without string-only keys, arbitrary-value keys, and memory-safe caching tied to
an object’s lifetime.
Set: unique-value collections
A Set is an unordered, unindexed collection of unique values — unlike an array, a value either belongs to a
set or it doesn’t; duplicates are silently ignored:
let s = new Set(); // an empty set
let t = new Set([1, 2, 2]); // Set {1, 2} -- the duplicate 2 collapses
t.size; // => 2
s.add(1); // add() is chainable and returns the set
s.add(1); // no-op: 1 is already a member
s.add("1"); // a distinct member -- Set compares with strict equality (===)
s.size; // => 2
s.has(1); // => true
s.delete(1); // => true (removed); returns false if the value wasn't present
s.clear(); // remove every member
The constructor accepts any iterable (an array, a string, another Set), which makes Set a convenient way to
deduplicate an array:
let unique = [...new Set([1, 2, 2, 3, 3, 3])]; // [1, 2, 3]
Because Set membership is checked by identity/strict-equality, two distinct objects or arrays with the same
contents are never considered duplicates — only a reference to the exact same object counts as a match.
Set is iterable in insertion order, and implements forEach() the same way arrays do:
let primes = new Set([2, 3, 5, 7]);
for (const p of primes) {
console.log(p);
}
primes.forEach(p => console.log(p));
Math.max(...primes); // => 7 -- spread a set directly into a function call
Prefer Set.prototype.has() over Array.prototype.includes() for membership testing on anything but a tiny,
fixed list: has() stays fast regardless of how many elements the set holds, while includes() scans the array
linearly.
Map: keys beyond strings
A Map associates arbitrary keys — not just strings — with values, filling the gap plain objects leave (an
object’s keys are always coerced to strings, and every object inherits properties like toString that can
collide with intentional keys):
let m = new Map();
m.set("one", 1).set("two", 2); // set() is chainable
m.get("two"); // => 2
m.get("missing"); // => undefined
m.has("one"); // => true
m.size; // => 2
m.delete("one"); // => true
m.clear();
The constructor accepts an iterable of [key, value] pairs, so Object.entries() converts a plain object into a
map in one step:
let n = new Map([["one", 1], ["two", 2]]);
let fromObject = new Map(Object.entries({x: 1, y: 2}));
Map iterates [key, value] pairs in insertion order — destructure them directly in a for…of loop, or pull
just one side with keys()/values():
for (const [key, value] of n) {
console.log(key, value);
}
[...n.keys()]; // => ["one", "two"]
[...n.values()]; // => [1, 2]
// forEach's callback takes (value, key) -- value first, by analogy with Array's (element, index)
n.forEach((value, key) => console.log(key, value));
Like Set, a Map compares keys by identity: an object literal used as a key is only ever equal to that same
object reference, never to a different object with matching properties.
WeakMap and WeakSet
WeakMap and WeakSet mirror Map and Set, but hold their object keys/members weakly — a weak reference
does not keep an object alive for garbage-collection purposes, so an object used only as a WeakMap key (or
WeakSet member) can still be reclaimed once every other reference to it is gone.
That memory-safety guarantee comes with restrictions:
-
Keys/members must be objects or arrays — primitives aren’t subject to garbage collection and can’t be used.
-
Only
get()/set()/has()/delete()(WeakMap) oradd()/has()/delete()(WeakSet) are implemented — neither type is iterable, and neither has asizeproperty, since either could change at any moment as the garbage collector runs.
const cache = new WeakMap();
function expensiveComputation(obj) {
if (cache.has(obj)) return cache.get(obj);
const result = /* ... expensive work based on obj ... */ obj;
cache.set(obj, result);
return result;
}
This WeakMap-backed cache never leaks memory: once obj becomes otherwise unreachable, its cache entry is
collected along with it — something a plain Map cache cannot offer, since a Map’s strong references would
keep every cached object alive for the life of the cache itself. `WeakSet is used less often, typically to
"brand" objects (mark them as having some property or type) without preventing their collection.
When to reach for each
-
Setover an array — when you need fast, order-preserving membership testing and don’t care about duplicates or indexed access (deduplication, tracking "seen" values, tag sets). -
Mapover a plain object — when keys aren’t naturally strings (objects, functions,NaN), when keys are only known at runtime and might collide with inherited property names, or when you need a reliablesizeand guaranteed insertion-order iteration. -
WeakMap/WeakSetoverMap/Set— when the collection associates data with objects you don’t own the lifetime of, and you want that association to disappear automatically rather than pin those objects in memory for as long as the cache exists.