- Object iterators
- Symbol.iterator
- Next()
- Making a class iterable
- Generator functions and yield
Most things in JavaScript that you can loop over (like arrays, strings, maps, sets) are iterable: they can provide iterators for their contents.
An iterator is a special object in JavaScript that helps you go through a list of items one at a time.
Object iterators
Unlike arrays or maps, regular objects are not iterable with
for...of. If you try this:const user = { name: "Alice", age: 30 } for (const value of user) { console.log(value) }
You’ll get an error:
TypeError: user is not iterable
That’s because plain objects don’t have a built-in iterator. But JavaScript gives you other tools to loop over them.
Object.keys()
You can use
Object.keys(obj) to get an array of the object’s keys, and then loop over it:const user = { name: "Alice", age: 30 } const keys = Object.keys(user) for (const key of keys) { console.log(key) }
This prints:
name age
Object.values()
To loop over the values, use
Object.values():const user = { name: "Alice", age: 30 } const values = Object.values(user) for (const value of values) { console.log(value) }
This prints:
Alice 30
Object.entries()
If you want both the key and the value, use
Object.entries():const user = { name: "Alice", age: 30 } const entries = Object.entries(user) for (const [key, value] of entries) { console.log(`${key} is ${value}`) }
This prints:
name is Alice age is 30
Even though objects aren't iterable directly, these methods give you full access to their contents in a way that works well with
for...of.But how do iterators work?
Symbol.iterator
The secret behind all iterables is a special symbol called
Symbol.iterator.This symbol is a built-in key that tells JavaScript: “This object can be iterated.”
When you call
myIterable[Symbol.iterator](), JavaScript gives you back an iterator object with a .next() method.Let’s see what that looks like:
const colors = ["red", "green", "blue"] const iterator = colors[Symbol.iterator]() console.log(iterator.next()) // { value: 'red', done: false }
Every call to
.next() gives you the next value. When it’s done, it returns:{ value: undefined, done: true }
next()
The
.next() method is used to get the next item from the sequence.Each time you call
.next(), you get an object with two keys:value: the current itemdone: a boolean that tells you if the iteration is over
Let’s do a full example:
const names = ["Lina", "Tom", "Eva"] // declare an array const iterator = names[Symbol.iterator]() // use the Symbol.iterator function to get an iterator for this array let result = iterator.next() // get the first element of the array while (!result.done) { // repeat this loop until you reach the last element of the array, which is marked with { done: true } console.log(result.value) // print the value of each element result = iterator.next() // get the next element of the array }
This prints:
Lina Tom Eva
This is how a
for...of loop works under the hood: it uses this pattern with .next().You will get the same result with
const names = ["Lina", "Tom", "Eva"] for (const result of names) { console.log(result) }
Making a class iterable
You can also define your own iterable class by adding a
[Symbol.iterator]() method.Let’s say we want a class that represents a range of numbers, like from 1 to 5.
class Range { constructor(start, end) { this.start = start this.end = end } [Symbol.iterator]() { let current = this.start const end = this.end return { next() { if (current <= end) { const result = { value: current, done: false } current = current + 1 return result } else { return { done: true } } } } } } const myRange = new Range(1, 5) for (const num of myRange) { console.log(num) }
This prints:
1 2 3 4 5
Here’s what’s happening:
We defined a class
Range
Inside the class, we implemented [Symbol.iterator](), so JavaScript knows how to iterate it
The next() method gives back each number one by one
When we reach the end, it returns { done: true }Now our
Range class works like an array, and we can use it in any loop that expects an iterable.Generator functions and yield
To make it easier to create iterators, JavaScript gives you generator functions, using the
function* keyword (it's function with a * at the end) and the yield keyword.Let’s try:
function* numberGenerator() { yield 1 yield 2 yield 3 } const iterator = numberGenerator() console.log(iterator.next()) // { value: 1, done: false } console.log(iterator.next()) // { value: 2, done: false } console.log(iterator.next()) // { value: 3, done: false } console.log(iterator.next()) // { value: undefined, done: true }
Each
yield gives back a value, and pauses the function until the next .next() is called.You can also loop over a generator with
for...of:for (const num of numberGenerator()) { console.log(num) }
This prints:
1 2 3