A number of facilities in the language are defined in terms of iterators.
This includes syntax such as the for..of loop as well as the spread (...values) of arrays, maps, and sets.
That said you almost never have cause to directly manipulate an iterator itself.
However, there are situations where being able to do so is essential.
For example, imagine we want to write a zip function that combines the corresponding elements of several iterables.
Since iterables can be infinite, we can't create an array and return it. On the one hand we can't allocate an infinite amount of memory and on the other we can't limit our input to finite sequences.
In this case, directly manipulating the iterator gives us exactly what we need
function* zip(iterables = []) { if (iterables.length < 2) { throw RangeError('at least 2 iterables must be provided'); } const iterators = iterables.map(iterable => iterable[Symbol.iterator]); while (true) { const iterations = iterators.map(iterator => iterator.next()); if (iterations.some(iteration => iteration.done)) { return; } yield iterations.map(iteration => iteration.value); } }
That is zip takes an array of 2 or more iteratables and consumes them element wise. It yields their corresponding elements together and ceases when any of them are done.
The above example is fairly complex but has the advantage of not being contrived.
These sorts of scenarios are important but relatively rare making direct use of iterators the exception rather than the rule.
for..ofor...values? Those are iterator driven. Things likeArray.prototype.mapare not but are very useful. I would turn it around and ask why you would ever use an index drivenforloop?for..ofand...valuesalso. My doubt is why we need to writelet iteratorVar = someArr[Symbol.iterator]();to loop instead of simply using available looping constructs