ITERATORS are not that scary...
According to the MDN web docs:
The for...of statement creates a loop iterating over iterable objects, , invoking a custom iteration hook with statements to be executed for the value of each distinct property of the object.
Ok... hm... but what's that Iterable object?
We use iteration almost every time we code... with stuff like: map, filter, for, forEach, ecc...
But the for...of statement is special, because:
Iterators (...) bring the concept of iteration directly into the core language and provide a mechanism for customizing the behavior of for...of loops.
So you can think that the for...of loop knows how to iterate over an array... but that's not true!.
We need the iterator object as "interface" to loop over it:
Now that we know this... let code it:
const dogs = ["labrador","chow-chow","cavalier"];
for (let breed of dogs)
{
console.log(breed);
}
//-> labrador, chow-chow, cavalier
Behind that... we've the iterator object that looks like this:
const dogs = ["labrador","chow-chow","cavalier"];
let iterator = dogs[Symbol.iterator]();
for (let breed of iterator )
{
console.log(breed);
}
//-> labrador, chow-chow, cavalier (the same result)
wait... WHAT?
Yes... that seems really strange, but It's not that bad.
Let's quote, again:
In order to be iterable, an object must implement the @@iterator method, meaning that the object (or one of the objects up its prototype chain) must have a property with a Symbol.iterator key. This function should return a new iterator for each call, however, this is not a requirement.
That Symbol.iterator
is the property every iterable object needs, to loop over It.
Iterable objects can be:
String
Array
Map
Set
nodeList
...
and all these possess that property, behind the scene.
Symbol.iterator
The property provides us, the next()
method, that works like this:
const dogs = ["labrador","chow-chow","cavalier"];
let iterator = dogs[Symbol.iterator]();
console.log(iterator.next())
//-> {value: "labrador", done: false}
Every time we call the next()
method, we loop over the iterable object.
Now we've got two keys:
value: "labrador"
(the current value of the iterable object)
done: false
(will be false until we've values inside of it!)
const dogs = ["labrador","chow-chow","cavalier"];
let iterator = dogs[Symbol.iterator]();
console.log(iterator.next())
//-> {value: "labrador", done: false}
console.log(iterator.next())
//-> {value: "chow-chow", done: false}
console.log(iterator.next())
//-> {value: "cavalier", done: false}
console.log(iterator.next())
//-> {value: undefined, done: true}
done:true
is the ending point for the iterable object.
There are some other powerful tools in JS that need the object to be iterable
//TAKE A LOOK AT THE SPREAD OPERATOR...
const number = 31;
let count = [...dogs];
//-> TypeError: dogs is not iterable
const dogs = "labrador";
let doggy = [...dogs];
console.log(doggy)
//-> ["l", "a", "b", "r", "a", "d", "o", "r"]
You've to be always careful about this, and use only iterable objects with tools like these!.
I want to say byebye adding a bit of spice:
Symbol.iterator
is a property... so... you can create your own iterable object if you want to!
For more info:
Top comments (2)
What about the speed on iterables? How do they compare with the ol'mighty for(;;)?
I think the big difference is in readability.
If we consider that many types of "loops" hide the "iterator" property as forEach, what makes a difference for us is, for example, that forEach can not be interrupted.
Most of the times βforβ is faster... but that kind of difference, on speed, is really not a big deal.