- Home
- /
- Tutorials
- /
- JavaScript Tutorial
- /
- JavaScript Custom Iterables
JavaScript Generators & Iterators
JavaScript Custom Iterables
Adding a Symbol.iterator method makes your own object work with for...of.
Adding a
Symbol.iteratormethod makes your own object work withfor of.
This is how you plug a custom type into every language feature that expects an iterable.
A generator is the easiest way to implement it.
Example
Example
javascript
const range = {
from: 1,
to: 3,
[Symbol.iterator]: function* () {
for (let i = this.from; i <= this.to; i++) {
yield i;
}
}
};
console.log([...range].join(","));The output is 1,2,3.
Making an Object Iterable
Add a method named [Symbol.iterator].
Making it a generator function is the simplest approach.
Once added, for of, spread and destructuring all work on it.
Syntax
Syntax
javascript
const obj = {
[Symbol.iterator]: function* () {
yield value;
}
};The square brackets are required; Symbol.iterator is a computed key.
Using for of Directly
No manual iterator handling is needed once this is in place.
Example
Example
javascript
const range = {
from: 1,
to: 4,
[Symbol.iterator]: function* () {
for (let i = this.from; i <= this.to; i++) {
yield i;
}
}
};
const values = [];
for (const n of range) {
values.push(n);
}
console.log(values.join(","));The output is 1,2,3,4.
A Class with Its Own Iteration
The same method works inside a class.
Example
Example
javascript
class Range {
constructor(from, to) {
this.from = from;
this.to = to;
}
*[Symbol.iterator]() {
for (let i = this.from; i <= this.to; i++) {
yield i;
}
}
}
const range = new Range(1, 3);
console.log([...range].join(","));Every instance of the class is now iterable.
Iterating Over Something More Interesting
A generator can yield anything, not just numbers.
Example
Example
javascript
class Playlist {
constructor(songs) {
this.songs = songs;
}
*[Symbol.iterator]() {
for (const song of this.songs) {
yield song.toUpperCase();
}
}
}
const playlist = new Playlist(["one", "two"]);
console.log([...playlist].join(","));The output is ONE,TWO.
Without a Generator
It can also be written by hand, returning an iterator object directly.
Example
Example
javascript
const range = {
from: 1,
to: 3,
[Symbol.iterator]() {
let current = this.from;
const to = this.to;
return {
next() {
return current <= to
? { value: current++, done: false }
: { value: undefined, done: true };
}
};
}
};
console.log([...range].join(","));Same result, more code - this is why the generator form is preferred.
Destructuring a Custom Iterable
Since it follows the protocol, destructuring works too.
Example
Example
javascript
const range = {
from: 10,
to: 20,
[Symbol.iterator]: function* () {
yield this.from;
yield this.to;
}
};
const [start, end] = range;
console.log(start + " to " + end);The output is 10 to 20.
Complete Example
Complete Example
html
<!DOCTYPE html>
<html>
<head>
<title>JavaScript Custom Iterables</title>
</head>
<body>
<h1>Custom Iterables</h1>
<p id="out"></p>
<script>
class Range {
constructor(from, to, step = 1) {
this.from = from;
this.to = to;
this.step = step;
}
*[Symbol.iterator]() {
for (let i = this.from; i <= this.to; i += this.step) {
yield i;
}
}
}
const evens = new Range(0, 10, 2);
document.getElementById("out").textContent = [...evens].join(", ");
</script>
</body>
</html>Try It Yourself
Run the above example in the Try It Editor.
Try a different step:
Change the step to 3 and see the sequence change.
Important Points
- A
[Symbol.iterator]method makes an object iterable. - Writing it as a generator is the simplest approach.
- Once added,
for of, spread and destructuring all work. - Classes can define this the same way as plain objects.
- An iterator can also be written by hand, without a generator.
Conclusion
This is how your own data types integrate with the rest of JavaScript.
A generator turns what would be fiddly code into a few natural lines.
Any class representing a collection is a strong candidate for this.
