On this page · sections
JavaScript Collections, Iterables & Generators
Can you pick
Mapover an object, implementfor...of, and pause a workflow withyield?
Objects are not a general dictionary. Iteration is a protocol, not a for loop. Generators are functions that return iterators.
These questions test:
Map/Setvs object / arrayWeakMapand GCSymbol.iteratoryieldand lazy sequences- When a generator is the wrong API
Questions
Collections
Iteration
Senior-Level Follow-ups
Collections
1. Map vs Object, Set vs Array
Why This Matters
Object keys are strings or symbols. Map keys are any value, including objects.
Problem
When do you use Map/Set instead of objects and arrays?
Example
const obj = {};
obj[1] = "a";
obj["1"] = "b";
Object.keys(obj); // ["1"] — one keyconst map = new Map();
map.set(1, "a");
map.set("1", "b");
map.size; // 2Map preserves insertion order, has .size, and does not inherit Object.prototype.
Set is unique values with SameValueZero (NaN matches NaN). Membership is O(1) typical; array.includes is O(n).
Use an object for JSON-shaped records and known string keys. Use Map for dictionaries, caches keyed by object, or mixed key types.
Interview Answer
“I use
Mapwhen keys are not plain strings, when I need size/order without prototype keys, or when I mutate membership often. I useSetfor uniqueness and membership. I use objects for records.”
2. WeakMap and WeakSet
Why This Matters
Caches that key on DOM nodes or objects must not keep those objects alive forever.
Problem
What can be a WeakMap key, and what cannot you iterate?
Example
const meta = new WeakMap();
function tag(el, info) {
meta.set(el, info);
}When el is removed and unreachable, the entry can be collected.
Keys must be objects (or non-registered symbols in WeakMap — practically: objects). No .size, no iteration, no .clear on WeakSet/WeakMap in the usual API.
That missing iteration is the point: the engine must be free to drop entries.
Interview Answer
“
WeakMapholds a non-owning key. It is for metadata and caches whose lifetime should follow the object. I cannot enumerate it, so it is not a general Map. If I need iteration, I useMapand accept the retention.”
Iteration
3. The Iterator Protocol
Why This Matters
for...of, spread, and destructuring all call obj[Symbol.iterator]().
Problem
What must an iterable return, and how is that different from an iterator?
Example
const iterable = {
[Symbol.iterator]() {
let n = 0;
return {
next() {
if (n >= 3) return { done: true, value: undefined };
return { done: false, value: n++ };
},
};
},
};
[...iterable]; // [0, 1, 2]Iterable → has Symbol.iterator → returns an iterator
Iterator → has next() → { value, done }Arrays, Maps, Sets, strings, and arguments are iterable. Plain objects are not.
A well-known Symbol is how JS adds protocols without colliding with user keys: Symbol.iterator, Symbol.asyncIterator, Symbol.toStringTag, Symbol.toPrimitive.
Interview Answer
“An iterable exposes
Symbol.iterator, which returns an iterator withnext().for...ofis that protocol. I implement it on custom collections instead of inventing.each().”
4. Generators and yield
Why This Matters
A generator is iterator sugar plus pause/resume.
Problem
What does function* return, and when does the body run?
Example
function* range(max) {
for (let i = 0; i < max; i++) {
yield i;
}
}
const it = range(3);
it.next(); // { value: 0, done: false }
[...range(3)]; // [0, 1, 2]The body starts on the first next(). Each yield pauses and hands a value out.
function* twoWay() {
const x = yield 1;
yield x + 1;
}
const g = twoWay();
g.next(); // { value: 1 }
g.next(10); // { value: 11 } — 10 becomes the result of yieldUse generators for lazy sequences, pagination, and custom iterables. Do not use them as a hidden state machine if a plain object would be clearer.
Interview Answer
“A generator function returns an iterator.
yieldpauses and produces a value. The nextnext(arg)resumes and suppliesargas the result ofyield. It is lazy: nothing runs untilnext().”
Senior-Level Follow-ups
5. Why Object.keys Misses Map
Object.keys(new Map([["a", 1]])); // []Map data is internal. JSON.stringify also produces {}.
Clone and equality must handle Map/Set explicitly — Deep Clone & Equality.
6. return(), throw(), and Cleanup
function* withFinally() {
try {
yield 1;
yield 2;
} finally {
console.log("cleanup");
}
}
const g = withFinally();
g.next();
g.return();
// cleanupfor...of calls iterator.return() on break. That is why try/finally inside a generator is a real resource hook.
7. Async Iteration
for await (const chunk of readable) {
consume(chunk);
}This calls obj[Symbol.asyncIterator](). next() returns a Promise of { value, done }.
async function* yields Promises. Use it for paginated APIs. Prefer streams when backpressure matters.
Final Interview Strategy
1. Map for dictionaries; object for records
2. Set for membership; array for sequences
3. WeakMap when the key should not pin memory
4. for...of is Symbol.iterator
5. Generators implement iterators lazily
6. Map is invisible to Object.keys
7. return() runs finally on early exitSenior-level signal: Choose the collection by key identity and lifetime, then implement iteration as a protocol — not as a custom
forEachyou invented.