From "You Don't Know JS: Types & Grammar"
🎧 Listen to Summary
Free 10-min PreviewIteration and Data Consumption
Key Insight
Programs are fundamentally built to process data, and the patterns used to step through this data significantly impact readability. The iterator pattern offers a standardized approach to consuming data incrementally, one segment at a time, rather than processing an entire dataset simultaneously. This is especially useful for large collections, such as 100 or 1000 database rows, which necessitate iterative processing. An 'iterator' is a data structure linked to an underlying source, exposing a `next()` method that returns the subsequent data piece; completion is signaled by a special value or exception. This standardization of iterative data handling results in cleaner, more understandable code, avoiding custom approaches for every data structure. ES6 formalized this with a specific protocol, defining that `next()` returns an 'iterator result' object containing `value` and a boolean `done` property, which is `false` until the iteration is finished.
Beyond manual invocation of `next()`, ES6 provides convenient mechanisms for consuming iterators. The `for..of` loop directly iterates over an iterator, as seen in `for (let val of it) { console.log(`Iterator value: ${ val }`); }`, which is significantly more readable than its manual loop equivalent. Another primary consumer is the spread (`...`) operator. In its 'spread' form, it unpacks an iterator into an array, like `var vals = [ ...it ];`, where each iterated value becomes an array element. Alternatively, it can spread an iterator into a function's argument list, as in `doSomethingUseful( ...it );`, with each value occupying an argument position. Both `for..of` and the spread operator adhere to the ES6 iteration-consumption protocol.
The iteration-consumption protocol technically operates on 'iterables,' which are values capable of being iterated over. An iterable automatically generates a new iterator instance each time it's consumed, allowing it to be iterated multiple times independently. ES6 designated core JavaScript data structures as iterables, including strings, arrays, maps, and sets. For instance, an array `var arr = [10, 20, 30];` can be directly used with `for (let val of arr) { /* ... */ }`. The spread operator enables shallow-copying arrays, like `var arrCopy = [ ...arr ];`, and allows iterating string characters into an array, e.g., `[ ..."Hello world!" ]` yields `[ "H", "e", "l", "l", "o", " ", "w", "o", "r", "l", "d", "!" ]`. Maps iterate over `entries` (key/value tuples) by default, but built-in iterables generally provide `keys()`, `values()`, and `entries()` methods for specific iteration needs. Custom data structures can also conform to this protocol, enhancing code readability and compatibility with standard iteration tools; notably, an iterator itself is an iterable that returns itself when an iterator instance is requested.
📚 Continue Your Learning Journey — No Payment Required
Access the complete You Don't Know JS: Types & Grammar summary with audio narration, key takeaways, and actionable insights from Kyle Simpson.