Making Objects Iterable in JavaScript

Uriel Rodriguez
The Startup
Published in
5 min readJan 5, 2021

--

Usually when we think of a data structure being iterable, we think of arrays and strings. JavaScript provides many ways to iterate over an array or string, ranging from the standard for-loop to more modern variation like the ES6 for/of loop. However, because objects are not inheritently iterable, JavaScript has defined the for/in loop which is specifically for iterating over objects. The for/in loop iterates over each property name or key in the object, by which then you could access the value. The following examples show how an object can be iterated over using both the for/of and the for/in loop.

const obj = { someProp: "someValue", anotherProp: "anotherValue" };for (let key of Object.keys(objKeys)) {
console.log(obj[key]);
}
for (let key in obj) {
console.log(obj[key]);
}

These examples show the more common ways an object can be iterated over. If there wasn’t a concern or need to access an object’s property name, then the for/of loop could be utilized in with the static method entries() which provides an iterable structure, an array of arrays in which the values could be accessed directly.

const obj = { someProp: "someValue", anotherProp: "anotherValue" };for (let [k, v] of Object.entries(obj)) {
// iterate over the returned array from Object.entries
// destructure the current element, an array with two elements, the key and value of the object "obj"
console.log(v);
}

In this manner, we can iterate over the object and only work with the values and avoid having to write out the object property access notation. These examples show how to iterate over objects, but the objects were not iterated over directly as arrays, or strings would have. For example:

const arr = [1, 2, 3];
const str = "string";
for (let num of arr) {
console.log(num);
}
for (let char of str) {
console.log(char);
}

For a data structure to be considered iterable, the for/of loop must be able to iterate over it. This example shows how the array and string were directly iterated over, as opposed to the examples with the object which only worked with the for/of loop using a returned array from Object.keys or Object.entries. In order to make objects iterable, we need to first understand how iteration work in JavaScript.

When an iterable data structure, like an array, string, array of arrays, and so on, are iterated over with the for/of loop, a special method within their definition is invoked, the [Symbol.iterator]() method. First, to demystify this particular syntax, the brackets around the “Symbol.iterator” is a computed property name, or a value that is returned when JavaScript accesses “Symbol.iterator”. So the method name is computed by JavaScript, and the returned value is the method’s name that is used, but which is not known in order to hard code it. The presence of this method is what makes a data structure iterable, and it does so because the method returns the iterator interface.

Passing an array to the for/of loop invokes the [Symbol.iterator]() method which returns the iterator interface, or an object which accesses the values of the iterable data structure, the array. The iterator object, the “iterator” is what iterates over the data structure by invoking a next() method which returns another object containing the desired value, or element in the case of an array. For example:

let arr = [1, 2, 3];for (let num of arr) {
console.log(num);
}
// under the hood
arr[Symbol.iterator]();
// returns an iterator - an object
let arrIterator = arr[Symbol.iterator]();
// the iterator object or interface invokes a next method
arrIterator.next();
// returns another object - iteration result containing the element
arrIterator.next(); //=> { value: 1, done: false }
// the "value" property on the returned iteration result object is assigned to the "num" variable within the for/of loop
num = arrIterator.next().value;
console.log(num);

When the array is passed to the for/of loop, it triggers the process above. The actual iteration is performed by the returned iterator object from the [Symbol.iterator]() method. The iterator invokes the next() method and the iterator result’s value property is then logged. The other property within the iterator result, the “done” property specifies whether the iteration is complete, or if there are no longer any values to return.

We can now apply this understanding to making objects iterable. The most important thing that is needed is defining the [Symbol.iterator]() method within the object, because this is what triggers the iterator.

let obj = { 
prop: "value",
someProp: "someValue",
anotherProp: "anotherValue",
[Symbol.iterator]() {
return Object.values(this);
}
};
for (let val of obj) {
console.log(val);
}
// alternative - simulates a for/in loop
let obj = {
...
[Symbol.iterator]() {
return Object.keys(this);
}
};
for (let key of obj) {
console.log(obj[key]);
}

By defining the special iterator method, we have made the object iterable. It works directly with with for/of loop and without having to utilize a return from another method invocation like Object.keys, Object.values, Object.entries, and so on. When the object is iterated, the [Symbol.iterator]() is invoked and an array is returned which invokes

A more involved way to make an object iterable is to redefine the iterator method by returning a custom iterator object and definining a next() method within it.

let obj = { 
prop: "value",
someProp: "someValue",
anotherProp: "anotherValue",
[Symbol.iterator]() {
let values = Object.values(this);
let index = 0;
return {
next() {
if (index < values.length) {
let val = values[index];
index++;
return { value: val, done: false };
} else return { done: true };
}
};
}
};

This approach takes advantage of closures, by which the iterator object accesses the scope of the [Symbol.iterator]() method after invocation and uses its defined variables “values” and “index” in its own method body.

After following these examples, the main takeaway is that JavaScript uses a specific mechanism to carry out the iteration process. It all begins with invoking the [Symbol.iterator]() method on the data structure, and is followed by the returned iterator object invoking its defined next() method. In understanding this mechanism, you can create your own data structures, or classes of objects that are iterable and return specific values from the iterative process. To learn more about the iteration mechanism, I recommend reading Eloquent JavaScript and JavaScript The Definitive Guide.

--

--

Uriel Rodriguez
The Startup

Flatiron School alumni and Full Stack web developer.