Javascript Custom Iterables
A while back I was creating a URLPattern class that allows you to match a URL
Pattern to a URL. I allowed ', ' to act as an and operator (&&). In that way,
my class was the following structure:
{
0: {
"pattern": String,
"match": Function
},
1: {
"pattern": String,
"match": Function
},
...
n: {
"pattern": String,
"match": Function
},
length: n + 1
}I wanted someone to be able to do [...pattern] and get back:
Array<{ pattern: String; match: Function }>; // length = n + 1That is when I came across the Symbol.iterator property.
Symbol.iterator
From the MDN docs:
The well-known
Symbol.iteratorsymbol specifies the default iterator for an object. Used byfor...of.
The Symbol.iterator is a property of the builtin Symbol object that exists
as a property on the Array prototype, Set prototype, and many other builtin iterators.
It gets called whenever the ... (spread syntax) or Array.from is run on the
object or for...of.
Implementing in class
To use Symbol.iterator inside classes, define a method with the method name being
[Symbol.iterator]. Here is an example usage:
class A {
[Symbol.iterator]() {}
}An example that will give back [1, 2, 3] is shown below:
class A {
constructor() {
this.isNothing = true;
}
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
}Now the following results can occur:
const value = new A();
[...A]; // [1, 2, 3]Array-like iterator
If you want an iterator that would do the following:
[...new myClass({ 0: 1, 1: 2, 2: 3, length: 3 })]; // [1, 2, 3]You can either copy Array.prototype's iterator or build something similar. We
will see how to do both:
Copying
class myClass {
constructor(obj) {
Object.keys(obj).forEach((key) => {
this[key] = obj[key];
});
}
}
myClass.prototype[Symbol.iterator] = Array.prototype[Symbol.iterator];
[...new myClass({ 0: 1, 1: 2, 2: 3, length: 3 })]; // [1, 2, 3]What the Array.prototype iterator does is checks the length and yields each and
every index until the length is reached.
Let's try that by ourselves.
Rewriting
class myClass {
constructor(obj) {
Object.keys(obj).forEach((key) => {
this[key] = obj[key];
});
}
*[Symbol.iterator]() {
let currentIndex = 0;
while (currentIndex < this.length) {
yield this[currentIndex];
currentIndex++;
}
}
}This defines a generator function that will keep track of the index that it was on and increment that every time. The index's value will then be yielded.

