Briefly
An iterator is an object that can access the elements of a collection one at a time while keeping track of its current position within that sequence.
In other words, an iterator is a mechanism that allows moving (iterating) through the elements of a collection in a certain order and makes them accessible.
How to Understand
An iterator in JavaScript is an object that returns the next element of a sequence through the next
method. This method returns an object with two properties:
value
— the value of the current element of the collection.done
— an indicator that shows whether there are more values in the collection available for iteration.
function makeIterator(array) { let nextIndex = 0 return { next: function () { if (nextIndex < array.length) { const result = { value: array[nextIndex], done: false } nextIndex++ return result } else { return { done: true } } } }}
function makeIterator(array) { let nextIndex = 0 return { next: function () { if (nextIndex < array.length) { const result = { value: array[nextIndex], done: false } nextIndex++ return result } else { return { done: true } } } } }
After creation, the iterator object can be used explicitly by calling the next
method:
let iterator = makeIterator(['Hello', 'world'])console.log(iterator.next().value)// 'Hello'console.log(iterator.next().value)// 'world'console.log(iterator.next().done)// true
let iterator = makeIterator(['Hello', 'world']) console.log(iterator.next().value) // 'Hello' console.log(iterator.next().value) // 'world' console.log(iterator.next().done) // true
Once the next
method finishes iteration, it returns { done
. This is a signal that the iteration is complete.
Why is it Needed
Almost everywhere iteration is needed, it is implemented through iterators. This includes not only strings and arrays, but also other data structures. Modern JavaScript has added a new concept of "iterable" objects, for instance, Map
, introduced in ES6. This allows iterating over an iterable object in a for
loop:
for (let value of ['a', 'b', 'c']) { console.log(value) // a // b // c}
for (let value of ['a', 'b', 'c']) { console.log(value) // a // b // c }
Suppose we have an object person
representing a dataset:
const person = { name: 'Mark', age: 30, gender: 'male', interests: ['music', 'fishing'],}
const person = { name: 'Mark', age: 30, gender: 'male', interests: ['music', 'fishing'], }
To make such an object iterable and allow for
to work with it, we define Symbol
in it:
person[Symbol.iterator] = function () { const properties = Object.keys(this) let count = 0 return { next() { if (count < properties.length) { const key = properties[count] let result = { done: false, value: person[key] } count++ return result } else { return { done: true } } }, }}
person[Symbol.iterator] = function () { const properties = Object.keys(this) let count = 0 return { next() { if (count < properties.length) { const key = properties[count] let result = { done: false, value: person[key] } count++ return result } else { return { done: true } } }, } }
Let's make sure that the person
object is indeed iterable:
for (let x of person) { console.log(x) // Mark, 30, male, ['music', 'fishing']}
for (let x of person) { console.log(x) // Mark, 30, male, ['music', 'fishing'] }
Built-in Iterators
In some cases, the iterator interface is called by default. Such objects as String
, Array
, Map
and Set
are iterable because their prototypes contain Symbol
.
Where Else Iterators Are Found
Destructuring
When destructuring, the iterator is used to access elements of a collection:
const [a, b] = new Set(['a', 'b', 'c'])// a// b
const [a, b] = new Set(['a', 'b', 'c']) // a // b
Array.from()
Array
allows converting an iterable object into an array:
const arr = Array.from(new Set(['a', 'b', 'c']))// ['a', 'b', 'c']
const arr = Array.from(new Set(['a', 'b', 'c'])) // ['a', 'b', 'c']
Spread Operator
The spread operator also calls the iterator interface by default:
const arr = [...new Set(['a', 'b', 'c'])]// ['a', 'b', 'c']
const arr = [...new Set(['a', 'b', 'c'])] // ['a', 'b', 'c']
Map
, Set
The constructors for Map
and Set
convert iterable values into Map
and Set
respectively:
const map = new Map([ ['uno', 'one'], ['dos', 'two'],])map.get('uno')// onemap.get('dos')// twoconst set = new Set(['red', 'green', 'blue'])set.has('red')// trueset.has('yellow')// false
const map = new Map([ ['uno', 'one'], ['dos', 'two'], ]) map.get('uno') // one map.get('dos') // two const set = new Set(['red', 'green', 'blue']) set.has('red') // true set.has('yellow') // false