Create Iterable Objects
Version: ES2015
Level: Advanced
We’ve seen that you can make iterable objects (generators) using generator functions, but you can also turn any ordinary object into an iterable. To do this, all you need is to have a Symbol.iterator
property on the object which is a function that returns an iterator object when called. The returned iterator will be used to generate the iterable values within that object. The easiest way to do this? With another generator function of course!
let accounts = {
checking: 5,
savings: 0.25
};
for (let balance of accounts) { // TypeError: not iterable
console.log(balance);
}
// making accounts an iterable
accounts[Symbol.iterator] = function * () {
for (let key in this) {
yield `${key}: $${this[key].toFixed(2)}`;
}
};
for (let balance of accounts) { // now works
console.log(balance);
}
/* logs:
checking: $5.00
savings: $0.25
*/
Even though objects are not normally iterable, once the accounts
object was given the Symbol.iterator
property, it became an iterable and was able to work within the for...of
. It’s the existence of a Symbol.iterator
property that makes any object an iterable.
Objects like arrays are inherently iterable because they already have this property. In the case of arrays, their iterator method is the same as their values()
method.
let array = [1, 2, 3];
console.log(typeof array[Symbol.iterator]); // function
console.log(array[Symbol.iterator] === array.values); // true
The same applies to generator objects created by generator functions. What’s interesting about generator objects is that their Symbol.iterator
is simply a function that returns the generator object.
function * makeOne () {
yield 'There can be only one';
}
let highlander = makeOne();
console.log(highlander[Symbol.iterator]() === highlander); // true
If you ever want to make an object an iterable, or even change the default iterable behavior, all you need to do is to define its Symbol.iterator
.
And in case you were wondering, the syntax for specifying a Symbol.iterator
generator in an object literal (or class) directly would be as follows, using the accounts
example from earlier:
let accounts = {
checking: 5,
savings: 0.25
* [Symbol.iterator] () { // generator function
for (let key in this) {
yield `${key}: $${this[key].toFixed(2)}`;
}
}
};
More info: