ES6 Iteration Protocol
This is the part of a series of blogs on the new features on the upcoming ECMAScript 6 (ES6) specification which JavaScript implements.
In this blog we focus on the iteration protocol introduced in ES6.
Iteration #
Lets take a quick programming 101 background. An iterator is basically a sequence generator. A sequence is an ordered list of values whose order and uniqueness does not matter. A trivial example of a sequence is the Array
.
So an iterator is just an abstraction of, for example, a for loop
. It has certain functions such as prev
, next
, valid
, key
, and value
to go through the sequence, iterating through the elements.
We do the iteration to save memory by only generating values that are needed when they are needed. This is done by dynamically performing instructions based on the iteration.
Think about a for loop that ends when i > 5
and breaks;
rather than going through the entire array checking if the validity statement (that i > 5
) still holds.
Iterable Protocol #
The Iterable Protocol allows us to define or customize the object’s iteration behavior such as when it is looped over in a for .. of
construct.
For an object to be iterable
, it must implement the @@iterator method by defining a property with a Symbol.iterator
key.
Some native objects are inherently iterable because of their prototype inheritance such as String
, Array
, TypedArray
, Map
, and Set
.
Example of a defined iterable:
var hermioneGranger = {};
hermioneGranger[Symbol.iterator] = function* () {
yield 'Hermione';
yield 'Jean';
yield 'Granger';
};
for (let name of hermioneGranger) {
console.log(name);
}
// "Hermione"
// "Jean"
// "Granger"
There are also builtin APIs that take on iterable objects such as
Map
,WeakMap
,Set
, andWeakSet
. Learn more about them at my previous blog.
To use iterable objects however you can use the natively exposed methods of iterating through the iterable objects such as for-of loops (as seen above), spread operator, yield*, and destructuring assignment.
Example:
/* For Of Loop */
for (let name of hermioneGranger) {
console.log(name);
}
// "Hermione"
// "Jean"
// "Granger"
/* Spread Operator */
[...hermioneGranger] // ["Hermione", "Jean", "Granger"]
/* Yield */
// From the yield function above
hermioneGranger().next(); // { value: "Hermione", done: false }
hermioneGranger().next(); // { value: "Jean", done: false }
hermioneGranger().next(); // { value: "Granger", done: false }
hermioneGranger().next(); // { done: true }
/* Destructuring Assignment */
[a, b, c] = new Set(hermioneGranger);
a; // "Hermione"
Iterator Protocol #
The Iterator Protocol defines a standard way to produce a sequence of values.
For an object to be an iterator
it must implement the next()
method with the following constraints:
next()
- A zero arguments function that returns an object with two properties
- done (boolean)
- is
true
if it is at the end of the iterated sequence - is
false
if the iterator was able to produce the next value of the sequence - done can be omitted if done is false
- is
- value (any)
- any JavaScript value returned by the iterator
- value can be omitted if done is true
- done (boolean)
Example:
function idMaker(){
var id = 0;
return {
next: function(){
return {value: id++, done: false};
}
};
}
var it = idMaker();
console.log(it.next().value); // 0
console.log(it.next().value); // 1
console.log(it.next().value); // 2
console.log(it.next().done); // false
console.log(it.next().value); // 4
Differences #
Inherently iterators and iterables are two different things. However an object could be both an iterator and an iterable at the same time
Example:
var name = ['harry', 'james', 'potter'];
var nameEntries = name.entries();
nameEntries.toString(); // "[object Array Iterator]"
nameEntries === nameEntries[Symbol.iterator](); // true
A perfect example of this is a generator object which is both an iterator and is iterable.