CHAPTER 10
Iterators are objects that can traverse a collection. They enable custom iteration like the .NET CLR IEnumerable or Java Iterable objects. They can generalize for..in to custom iterator-based iteration with for..of. They also don’t require realizing an array, thus enabling lazy design patterns like LINQ.
The following is an example of an iterator definition for producing Fibonacci numbers:
Code Listing: 137
let fibonacci = { [Symbol.iterator]() { let pre = 0, cur = 1; return { next() { [pre, cur] = [cur, pre + cur]; return { done: false, value: cur } } } } } |
The first thing to notice is the usage of the Symbol.iterator. This keyword is necessary when defining iterators. Based on the interface, you will notice that the iterator returns a single function, next. It is also interesting to note that internal to the next function, we see examples of destructuring. This is more convenient than writing the equivalent in multiple statements. Finally, we see that the next function returns an object literal with two properties.
Let’s move to the next section and see how this iterator is used.
Now that we have an iterator defined, let’s see how we can use it. Consider the following code example:
Code Listing: 138
for (var n of fibonacci) { // truncate the sequence at 100 if (n > 100) break; console.log(n); } |
As you can see, the syntax of for..of is the same as for..in. We shortened the sequence so that we could show the output without filling a whole page. The following is the output when we execute the preceding code:
Code Listing: 139
1 2 3 5 8 13 21 34 55 89 |
Let’s look at another example using for..of with an array:
Code Listing: 140
for (let element of [1, 2, 3]) { console.log(element); } |
The preceding code could have been written as follows as well:
Code Listing: 141
let elements = [1, 2, 3]; for (let element of elements) { console.log(element); } |
The following is the output for the preceding code:
Code Listing: 142
1 2 3 |
When we compare for..of and for..in, we will see that for..in iterates over property names while for..of iterates over property values.
Let’s take one more look at iterators by looking at the built-in iterable for String. Consider the following code:
Code Listing: 143
let someString = "hi"; var iterator = someString[Symbol.iterator](); console.log(iterator + "");
console.log(iterator.next()); console.log(iterator.next()); console.log(iterator.next()); |
Here, we are creating a string variable, someString, that contains the string hi. We next create an iterator off of the someString variable using the Symbol.iterator syntax. Finally, we log out the iterator. We are forcing it to use its toString function by concatenating an empty string. The sequence of calls for iterator.next() shows us exactly what is contained from the returned object literals.
The following is the output for the preceding code:
Code Listing: 144
[object String Iterator] { value: 'h', done: false } { value: 'i', done: false } { value: undefined, done: true } |
As you can see, this is identical to the signature that we implemented in our custom Fibonacci iterator. Hopefully this gives you a better understanding as to how iterators work.