Now we can iterate over an instance of
Log
just as if it were an array:
const
log
=
new
Log
();
log
.
add
(
"first day at sea"
);
log
.
add
(
"spotted whale"
);
log
.
add
(
"spotted another vessel"
);
//...
// iterate over log as if it were an array!
for
(
let
entry
of
log
) {
console.log(
`
${
entry
.
message
}
@
${
entry
.
timestamp
}
`
);
}
In this example, we’re adhering to the iterator protocol by getting an iterator from the
messages
array, but we could have also written our own iterator:
class
Log
{
//...
[
Symbol
.
iterator
]() {
let
i
=
0
;
const
messages
=
this
.
messages
;
return
{
next
() {
if
(
i
>=
messages
.
length
)
return
{
value
:
undefined
,
done
:
true
};
return
{
value
:
messages
[
i
++
],
done
:
false
};
}
}
}
}
The examples we’ve been considering thus far involve iterating over a predetermined
number of elements: the pages in a book, or the messages to date in a log. However,
iterators can also be used to represent object that never run out of values.
To demonstrate, we’ll consider a very simple example: the generation of Fibonacci
numbers. Fibonacci numbers are not particularly hard to generate, but they do
depend on what came before them. For the uninitiated, the Fibonacci sequence is the
sum of the previous two numbers in the sequence. The sequence starts with 1 and 1:
the next number is 1 + 1, which is 2. The next number is 1 + 2, which is 3. The fourth
number is 2 + 3, which is 5, and so on. The sequence looks like this:
1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144,...
The Fibonacci sequence goes on forever. And our application doesn’t know how
many elements will be needed, which makes this an ideal application for iterators.
The only difference between this and previous examples is that this iterator will never
return
true
for
done
:
178 | Chapter 12: Iterators and Generators