That snippet of code prints out the first few Fibonacci numbers to the console, as you might expect.

Another way to go through the elements is to use the forEach method
on an array. When you pass a function argument to forEach, forEach will visit each element in the array. As it visits each element, it invokes your
function argument and passes it that element.

Here, we’ve passed an anonymous arrow function to forEach, but you can also use named functions or regular (non-arrow) anonymous functions. The arrow function expects
to receive the current item being visited by forEach and gives it the
label number, which is then used inside the body of the arrow function.
Some developers prefer forEach because they find it more expressive than
a for-loop.

Iterable Iterators and Potent Potables

Generators (things that produces iterators) and iterators
(things that can be iterated over) work based on two separate protocols: the iterable protocol and the
iterator protocol.

A generator function is really just syntactic sugar
for implementing the iterable protocol. This protocol
specifies that an object must have a method named
[Symbol.iterator]. (That’s a method whose name
is the ES6 constant Symbol.iterator.)

This [Symbol.iterator] method can return any object, as long
as it is an iterator, meaning that it implements the iterator protocol.

The iterator protocol says that an iterator needs to have
a next method, and that next should return an
object with a value property and a boolean done property.

Here is an example of an object literal version of our
generator function.

letastley={[Symbol.iterator](){returnthis;},values:[{value:"Never gonna give you up",done:false},{value:"Never gonna let you down",done:false},{value:"Never gonna run around and desert you",done:false},{value:"Never gonna make you cry",done:false},{value:"Never gonna say goodbye",done:false},{value:"Never gonna tell a lie and hurt you",done:false},{value:undefined,done:true}],next(){returnthis.values.shift();}};

The [Symbol.iterator] method simply returns a reference to itself, since
it also implements the next method. [Symbol.iterator] is not required
to return this — you are free to make your iterables separate from your
iterators. In this example, we made our iterable and iterator the same object.

You could use your iterable object like so:

for (let lalala of astley) {
console.log(lalala);
}

The for..of construct implicitly calls astley[Symbol.iterator] to get the iterator object. Then it calls the next method over and over until the
value of the done property is true.

The object literal version is much more verbose than our generator function.
But, it is a good option if you already have an object that encapsulates some
sort of computed sequence and you want to use it with for..of.

Summary

Generators were added to JavaScript in ES6. When combined with for..of, they provide an easy way to create and consume sequences of values.