Question presented to candidate:
"You have a custom class — say, a Range representing start/end/step — and for (const n of myRange) currently throws. What exactly makes an object 'iterable' in JavaScript, and how would you implement that using a generator method specifically, rather than hand-writing the iterator protocol by hand?"
What a strong answer should cover:
- An object is iterable if it has a method keyed by the well-known symbol
Symbol.iteratorthat returns an iterator — an object with anext()method returning{ value, done }.for...of, spread (...), destructuring, andArray.fromall work by calling this method internally. - 📌 Verified, not assumed: a plain class with no
Symbol.iteratorgenuinely threw a realTypeError("is not iterable") the instantfor...ofwas used on it — not a silent no-op. - 📌 Interview term: a generator method as
Symbol.iterator— writing*[Symbol.iterator]() { ... }on a class lets the method itself BE the iterator factory: calling it returns a real generator object, which already correctly implementsnext()/done/return()— the entire manual iterator-protocol boilerplate (a hand-writtennext()method tracking state, PLUS a hand-writtenreturn()method for cleanup) collapses into a single function usingyield. - 📌 Verified, not assumed — the real cleanup advantage: breaking out of a real
for...ofloop early genuinely triggered the generator method's ownfinallyblock automatically, with zero extra code — the identical cleanup behavior in a manually-written iterator object required a hand-writtenreturn()method, verified directly, to achieve the same result. - A precise answer names that this identical generator method genuinely works for spread, array destructuring, and
Array.fromtoo — since all of them consume the sameSymbol.iteratormethod, not something special tofor...ofalone.
Clarifying questions expected:
- "Does the underlying data need to be computed lazily (one value at a time, only as requested), or is it acceptable to already have the whole collection in memory?" — a generator's real, lazy, pull-based evaluation is a genuine advantage specifically for large or infinite sequences, not just a syntax preference.
- "Does any consumer need to iterate the SAME instance multiple times concurrently?" — a generator-based
Symbol.iteratormethod genuinely creates a fresh, independent generator on every call, so this works correctly by default, but is worth confirming.
Code / implementation expected: Yes — a real, running for...of/spread/destructuring/Array.from demonstration over a custom iterable class, plus a real proof that an early break genuinely triggers the generator's own cleanup code, is the concrete way to prove the mechanism rather than just describe it.