Since 2015, JavaScript has improved immensely.
It’s much more pleasant to use it now than ever.
In this article, we’ll look at JavaScript iterable objects.
Speed of the Iteration Protocol
The speed of the iteration protocol has been taken into account when this is created.
Memory management is fast when managing small objects.
JavaScript engine optimization iteration so that no intermediate objects are allocated.
Reuse the Same Iterable Object
We can use iterable multiple times.
So we can write:
const results = [];
const iterator = [1, 2, 3][Symbol.iterator]();
while (!(val = iterator.next()).done) {
results.push(val);
}
We get the iterator from the array.
And then we called it with our while
loop to get the results.
Iteration
There’re rules that govern the JavaScript iteration protocol.
There are some rules for the next
method.
As long as the iterator is returning a value next
, returns an object with the value
property.
And done
will be false
.
So we have:
{ value: x, done: false }
After the last value is iterated over, next
should return an object whose property done
is true
.
Iterables that Return Iterators
Iterables can return fresh iterators or return the same iterator.
If they return fresh iterators, then each ones return values from the start.
So if we have something like and array and we use:
function getIterator(iterable) {
return iterable[Symbol.iterator]();
}
to get the iterator, then we can compare them to see if they return the same iterator.
For instance, if we have:
const arr = ['a', 'b'];
console.log(getIterator(arr) === getIterator(arr));
Then the expression would log false
.
This means even though the array is the same, they return the same iterator.
Other iterables like generators return the same iterator each time.
If we have generator objects, we return the same generator each time it’s called:
function* genFn() {
yield 'foo';
yield 'bar';
}
const gen = genFn();
console.log(getIterator(gen) === getIterator(gen));
The genFn
is a generator function that returns a generator,
And when we get the iterator from the generator, we get the iterator from it and compare them and the expression logs true
.
So the same generator has the same iterator.
We can iterate over a fresh iterator multiple times.
For instance, we can write:
const arr = ['foo', 'bar', 'baz'];
for (const a of arr) {
console.log(a);
}
for (const a of arr) {
console.log(a);
}
to loop through the same array twice.
On the other hand, if we have a generator:
function* genFn() {
yield 'foo';
yield 'bar';
}
const gen = genFn();
for (const a of gen) {
console.log(a);
}
for (const a of gen) {
console.log(a);
}
then we only loop through it once even if we have 2 loops.
Closing Iterators
There’re 2 ways that an iterator can be closed.
An iterator can be closed with exhaustion or closing.
Exhaustion is when the iterator returned all the iterable values.
Closing is done by calling return
in the iterator function.
When we call return
, then next
won’t be called.
return
is an optional method.
Not all iterators have it.
Iterators that have a return
call is called closable.
return
should only be called if an iterator hasn’t exhausted.
This can happen if we loop through the for-of loop with break
, continue
, return
or throw
.
return
should produce an object that returns { done: true, value: x }
.
Conclusion
Iterable objects can have different variations.
They can return a single iterator or multiple instances.
They can also be closable.