Hey, I’m Mat, but ‘Wilto’ works too. I’m here to teach you JavaScript. Well, not here-here; technically I’m ready Piccalil.li’s JavaScript for everyone course to teach you JavaScript. The following is an excerpt from the Iterables and iterators module: the lesson about Iterators.
Iterators are one of JavaScript’s most linguistically confusing topics: sailing easy over what is already a pretty high bar. There are iterables — array, Set, Map and string — all of which follow the iterable protocol. To follow this protocol, an object must have the iterable interface. In practice this means that the object is a [Symbol.iterator]() method somewhere in the prototype chain. Iterable protocol is one of the two iteration protocols. The other iteration protocol is the iterator protocol.
See what I mean when I say this is linguistically loaded? Iterables implement the iterable iteration interface, and iterators implement the iterator iteration interface! If you can say that five times quickly, then you get the gist of it; easy, right?
No, listen, by the time you reach the end of this lesson, I promise it won’t be half as confusing as it might sound, especially with the context you get from the lessons that precede it.
A iterable object follows the iterable protocol, which just means that the object has a conventional method for creating iterators. The elements it contains can be looped for…of.
A iterator object follows the iterator protocol and the elements it contains are accessible consecutiveone by one.
Nasty repeat – a pun for which I do not forgive myself, nor do I expect you to forgive me – an iterator object follows the iterator protocol and the elements it contains are accessible consecutiveone by one. The Iterator protocol defines a standard way to produce a set of values, and optional return a value once all possible values have been generated.
To follow the iterator protocol, an object must—you guessed it—the iterator interface. In practice, this again means that a certain method must be available somewhere in the object’s prototype chain. In this case it is the next() method that iterates through the elements it contains one by one, returning an object each time that method is called.
To meet the iterator interface criteria, the returned object must contain two properties with specific keys: one with the key valuerepresenting the value of the current element, and one containing the key donea Boolean value that tells us whether the iterator has progressed past the last element in the data structure. That is not a difficult formulation that the editors have let slip: its value done is owned true only when there is a call next() results in an attempt to access an element past the last element in the iterator, not when accessing the last element in the iterator. Again, there’s a lot in print, but it makes more sense when you see it in action.
You’ve seen an example of a built-in iterator before, albeit short:
const theMap = new Map([ [ "aKey", "A value." ] ]);
console.log( theMap.keys() );
// Result: Map Iterator { constructor: Iterator() }
That’s right: while a Map object itself is an iterable, built-in Map method keys(), values()And entries() they all return Iterator objects. You’ll also remember that I looped through the users forEach (a relatively recent addition to the language). Used this way, an iterator is indistinguishable from an iterable:
const theMap = new Map([ [ "key", "value " ] ]);
theMap.keys().forEach( thing => {
console.log( thing );
});
// Result: key
All iterators are iterable; they all implement the iterable interface:
const theMap = new Map([ [ "key", "value " ] ]);
theMap.keys()[ Symbol.iterator ];
// Result: function Symbol.iterator()
And if you’re upset about the increasing blurring of the line between iterators and iterables, wait until you see this “top ten anime betrayal” video candidate: I’m going to demonstrate how to interact with an iterator using an array.
“BOO,” you surely cry, after being so betrayed by one of your oldest and most indexed friends. “Array is an iterableno iteraTor!” You’re both right to yell at me in general, and right when it comes to array in particular: an array is an iterable, not an iterator. Although all iterators are iterable, none of the built-in iterables are iterators.
However, if you mention that [ Symbol.iterator ]() method – the one that defines an object as an iterable – returns an iterator object created from an iterable data structure:
const theIterable = [ true, false ];
const theIterator = theIterable[ Symbol.iterator ]();
theIterable;
// Result: Array [ true, false ]
theIterator;
// Result: Array Iterator { constructor: Iterator() }
The same goes for Set, Map and – yes – even strings:
const theIterable = "A string."
const theIterator = theIterable[ Symbol.iterator ]();
theIterator;
// Result: String Iterator { constructor: Iterator() }
What we’re doing here manually: creating an iterator from an iterable using %Symbol.iterator% – is exactly how iterable objects work internally, and why they need to be implemented %Symbol.iterator% to are iterables. Every time you iterate through an array, you are actually iterating through an iterator created from that array. All built-in iterators Are iterable. All built-in iterables can be used for this create iterators.
Varied – preferablyeven because you don’t have to graze on it %Symbol.iterator% direct — you can use the built-in one Iterator.from() method to create an iterator object from any iterable object:
const theIterator = Iterator.from([ true, false ]);
theIterator;
// Result: Array Iterator { constructor: Iterator() }
You remember how I said that an iterator is a next() method (that returns a very specific object)? Call that next() method iterates through the elements contained in the iterator one by one, with each call returning an instance of that object:
const theIterator = Iterator.from([ 1, 2, 3 ]);
theIterator.next();
// Result: Object { value: 1, done: false }
theIterator.next();
// Result: Object { value: 2, done: false }
theIterator.next();
// Result: Object { value: 3, done: false }
theIterator.next();
// Result: Object { value: undefined, done: true }
You can think of this as a more controlled form of traversal than the traditional ‘wind it up and see how it goes’. for loops you’re probably used to – a method to access elements step by step, if necessary. Granted, you don’t to have to step through an iterator this way, since they have their own iterator Iterator.forEach method, which works exactly as you would expect – up to a point:
const theIterator = Iterator.from([ true, false ]);
theIterator.forEach( element => console.log( element ) );
/* Result:
true
false
*/
But there’s another big difference between iterables and iterators that we haven’t discussed yet, and for my money it actually goes a long way toward making linguistic feeling of the two. Maybe you should humor me a little here.
See, an iterable object is an object that is iterable. No, listen, stick with me: you can iterate over an array, and when you’re done with it, you can still iterate over that array. It is, by definition, an object that can be repeated; it is the essential nature of an iterable to be iterable:
const theIterable = [ 1, 2 ];
theIterable.forEach( el => {
console.log( el );
});
/* Result:
1
2
*/
theIterable.forEach( el => {
console.log( el );
});
/* Result:
1
2
*/
In a sense, an iterator object represents the singular action of iteration. Internal to an iterable, it is the mechanism by which the iterable is iterated each time that iteration is executed. As a standalone iterator object – whether you step through it using the next method or loop over its elements using forEach – once iterated, that iterator is past tense; it is repeated. Since they maintain an internal state, the essential nature of an iterator must be reiterated in singular form:
const theIterator = Iterator.from([ 1, 2 ]);
theIterator.next();
// Result: Object { value: 1, done: false }
theIterator.next();
// Result: Object { value: 2, done: false }
theIterator.next();
// Result: Object { value: undefined, done: true }
theIterator.forEach( el => console.log( el ) );
// Result: undefined
That makes for nice work when you use the Iterator constructor’s built-in methods to, for example, filter or extract part of an Iterator object:
const theIterator = Iterator.from([ "First", "Second", "Third" ]);
// Take the first two values from `theIterator`:
theIterator.take( 2 ).forEach( el => {
console.log( el );
});
/* Result:
"First"
"Second"
*/
// theIterator now only contains anything left over after the above operation is complete:
theIterator.next();
// Result: Object { value: "Third", done: false }
Once you reach the end of an iterator, it is finished iterating. Iterated. Past tense.
And that includes your time in this class. You might be relieved to hear that. I know this was quite a difficult course, but the good news is: this course is iterable, not an iterator. This step in your iteration through it – this lesson – may be over, but the point of this course is that you can iterate through it again. Don’t worry about having to memorize all this now; you can come back and watch this lesson again at any time.
Conclusion
I stand by what I wrote there, as surprising as it probably is: this lesson is a tough one, but listen, you have this. JavaScript for everyone is designed to take you inside the mind of JavaScript. Once you start to see how the cogs mesh together – see the fingerprints left by the people who built the language, and the good, bad and sometimes baffling decisions made along the way – no cause-or -ble or -tor will get in your way.

My goal is to give you the deep magic – the How and the Why of JavaScript, using the syntax you’re likely to encounter in your daily work, at your pace, and on your terms. If you’re new to the language, you’ll leave this course with a fundamental understanding of JavaScript that’s worth hundreds of hours of trial and error. If you’re a junior developer, you’ll complete this course with a depth of knowledge to rival any senior.
I hope to see you there.
(gg, yk)
#JavaScript #Iterators #Smashing #Magazine

