Why were they introduced ?
To process a sequence of data more efficiently
They make it possible to access data from a collection one at a time which will allow us to focus on what to do with that data rather than how to access that data.
JS didn't have the vest looping construct to iterate over a collection of data and there was a need to introduce a better
iteration protocol
.
Technical Definition of Iterables
and Iterators
It is a data structure that wants to make its elements accessible to the public.
It is an object that knows how to access items from a collection one at a time, while keeping track of its current position within that sequence.
Built-in Iterables
in JS are Strings
, Arrays
, Maps
, Sets
. There is a way to access elements one at a time from these data structures. But to access these there was a new looping construct (for...of
) introduced in the ES6
Specification.
this for...of
loop construct is easy to use and implement over the built-in objects as shown below.
// To access each character in a string
for (const ch of str) {
console.log(ch);
}
// To access each item in an array
for (const item of array) {
console.log(item);
}
// To access each K-V pair in a map
for (const [key, val] of mMap) {
console.log(`${key}: ${val}`);
}
// To access each element in a set
for (const elem of mSet) {
console.log(elem);
}
REMEMBER: Objects
in JS are NOT Iterable
by default. We can make an object iterable by implementing the iterable protocol. Let us now implement it, to convert an object .
const obj = {
// Every 'iterable' must have a Symbol called 'iterator'
[Symbol.iterator]: function () {
let step = 0;
const iterator = {
next: function () {
step++;
switch (step) {
case 1:
return { value: 'Hello', done: false };
case 2:
return { value: 'World', done: false };
default:
return { value: undefined, done: true };
}
},
};
},
};
You can create a custom iterable that gives a range of numbers from 1
to max
(specified by you).
const range = function (max) {
return {
[Symbol.iterator]: function () {
let idx = 0;
return {
next: function () {
return { value: ++idx, done: idx === max + 1 };
},
};
},
};
};
for (const a of range(10)) {
console.log(a);
}
IMPORTANT: The iterator mentioned above, can optionally also return a
return
property, which is called when the iterator stops prematurely (Like abreak
statement in thefor...of
loop). See below on how to use it.
const range = function (max) {
return {
[Symbol.iterator]: function () {
let idx = 0;
return {
next: function () {
return { value: ++idx, done: idx === max + 1 };
},
// This is not Compulsory
return: function () {
return {
done: true,
};
},
};
},
};
};
for (const a of range(10)) {
console.log(a);
if (a === 5) break;
}
Similarly there is an optional
throw
property as well.See MDN docs if you need more info on
Iteration Protocols
.