Published: 2017-12-07
Updated: 2017-12-26
Web: http://fritzthecat-blog.blogspot.com/2017/12/es6-iterator-and-iterable.html
Some built-in ES6 classes use the Iterator and Iterable "protocols". These are just naming conventions, but used by several new ES6 syntax elements:
In this article I won't cover generators or yield
.
Iterator and Iterable are their foundation, so let's first look at these.
Click onto one of the buttons to get an example script into the text area. Below the script you find an explanation.
An Iterable
can create an Iterator
, having a next()
function,
returning an object containing a value
object and done
boolean.
The syntax for defining an iterator function is a little cryptic
due to the fact that it must be a computed symbol-property holding a function.
But all in all iterators will make ES6 code shorter and more flexible.
An Iterator is an object that has a next()
function, with zero parameters.
This function must return an object with properties value
and done
, each time it is called.
The value
is the next sequence value to be used by the caller, done
is a boolean that is always false except when the iteration is over, then it is true.
When done
is true, the value
should not be used any more.
This example shows an Iterator that iterates over an array.
Every call increments the index pointing to the current element.
As soon as the next()
function has been called for all array-elements,
an object with done
false is returned, and the index is reset to be ready for next iteration.
The example is not really useful, because an Array is an Iterable itself.
It is just here to show the responsibility of an Iterator, also called
protocol.
An Iterator can be infinite.
The caller must know this and carefully use it in loops, because it never delivers done === true
.
Such makes sense for Id-generator singletons that return an unique id each time they are called.
This example shows an iterator that simply increments a counter.
The iterating while-loop must check how far it wants to loop - beware of infinite loops!
An Iterable is an object that returns an Iterator when calling the
function in its Symbol.iterator
property, like in
const iterator = testString[Symbol.iterator]()
.
The Symbol.iterator
is a static constant
used to define iterator-factory-functions in objects and classes by convention.
Several ES6 classes are Iterable, e.g. String, Array and Map, but not Object.
In Java we would see all implementers of the Iterable interface in an inheritance-tree,
but neither ES6 nor JavaScript have interfaces. Everything is convenience and naming-convention.
When not using the for-of loop, which implicitly uses the iterable's iterator,
it is possible to retrieve the iterator by using the expression
iterable[Symbol.iterator]()
, i.e. calling
the symbol-function representing a factory for iterators.
Here is how to implement an Iterable.
It shows the syntax you have to use when you want to implement an Iterable.
The function that returns the Iterator is the value of a
computed symbol-property.
It returns an object with a next()
function which delegates to the iterable given in constructor.
The next()
implementation must be an arrow-function here, because
only the arrow-function provides the new lexical this handling.
With the traditional object-syntax you would get an error due to a wrong this
pointer.
This example is not really useful, because it simply forwards to another Iterable.
Iterating the most popular iterables in for-of loops.
For-of loops require an iterable behind "of", like the
spread-operator requires it behind "...".
Mind the constructor capability of Map
.
You can build it using an array of tuple-arrays, or in a "fluent" way using set()
.
The for-of loop uses a destructuring-statement [ key, value ]
to put key and value of each map-entry into local loop-variables.
Testing if two separate iterators can be used in parallel, i.e. whether they have state stored in the common object they iterate.
ɔ⃝ Fritz Ritzberger, 2017-12-07