You Can’t Loop Over Objects With for-of in JavaScript
Understand the difference between looping over an object and looping over an array

A quick question. You can’t loop over {x: 1, y:2}
with a for-of loop. Why?
const obj = { x: 1, y: 2 };for (const key of obj) {
// ERROR
console.log(key);
}
If you don’t know the exact reason, you’ve come to the right place! In this post, I will talk about what is iterable.
Let’s First See What You Can Loop Over
First things first. In JavaScript, there are some values you can loop over with for-of and some values you can’t loop over. Typically, you can use for-of with arrays.
Strings can be looped over as well.
But, these values can’t be mapped over.
Once you execute the for-of that gives you an error, you can see that the error message is weird.
Uncaught TypeError: 123 is not iterable
None of the variables names are iterable but what does that mean?
An Iterator Is the One You Actually Need
Since ES6, a new feature has been added, Symbol
, which is for a unique value. In the Symbol
function, there is an object you can call, Symbol.iterator
. This is a function that returns the default iterator for an object.
When an object is created, JavaScript internally creates an object named %IteratorPrototype
and assigns it to the newly created object’s iterator object.
When this object is used by a for-of loop, for example, JavaScript checks if it has an iterator object, which inherits from %IteratorPrototype
, JavaScript’s internal core prototype for iteration. If it doesn’t exist, you’re given the following error.
Uncaught TypeError: 123 is not iterable
Meaning, %IteratorPrototype
has to be used by an object when it’s created so that it can be looped over, and the iterator object of an object, such as [1, 2, 3]
, is called Symbol.iterator
— its internal term in the specification is %%iterator
, but you can’t use this name in your code.
If Type(O) is not Object
, throw a TypeError
exception.
Symbol.iterator
Every mappable object has a Symbol.iterator
— Array, String, TypedArray, Map, and Set. You can explicitly use the iterator function to loop over it.
When you get the iterator object, it looks like this.
{
__proto__: {
next: f next(),
Symbol(Symbol.toStringTag): "String Iterator"
}
}
It has the next
function that moves the head of the pointer of the index of the object and the Symbol.toStringTag
method that defines how it prints out the value when its toString
is called.
If you call next
, it returns this.
{
value: "h",
done: false
}
done
is the property that shows whether all of the indexes are visited by the iterator, and value
is the value of the current index.
If you call next
again, the return value will be as follows.
{
value: "e",
done: false
}
Now you know why the value
is “e”
.
You can keep calling next
and check that the value
will be “l”, “l”, and “o”, thus its “hello”. If you call next
one more time, the return value will be a little bit different.
{
value: undefined,
done: true
}
The value
is now undefined and done
is true
.
Once the iterator loops over all of the indexes of strings or arrays or anything that has the Symbol.iterator
, its head that has been pointing to the indexes is now pointing to the empty space.
Thus, the value
would be undefined. And done
is changed to true
, since the head is pointing to the empty space.
Yet, you can keep calling next
, it’ll just give you the same response where the value
is undefined and done
is true.
Make the Object Iterable
Now, let’s create the object iterable, using Symbol.iterator
.
const obj = { x: 1, y: 2 };
It was the original object. What I’m going to do is to add the [Symbol.iterator]
function to make it loopable.
The code looks quite messy, but I’ll explain to you how this works.
next()
has the scope for this
that refers to within next()
. So, this
in next()
won’t look at the obj
.
So, first of all, I used Object.entries()
to get every entry in obj
.
// Object.entries(obj)
[
0: ["x", 1],
1: ["y", 2],
length: 2
]
Then, I made the index property for looping over the entries
— i: 0
.
if (this.i >= this.entries.length) {
return { done: true, value: undefined };
}
If i
is equal to or greater than the entries
length, which means that the iterator finishes looping over the entries
, it starts to return { done: true, value: undefined }
from the next iterator call.
Until the condition above is met, the return value will be:
{
done: false,
value: {
[this.entries[this.i][0]]: this.entries[this.i++][1]
}
}
Now, the data will be printed successfully.
const (let k of obj) {
console.log(k);
}// { x: 1 }
// { y: 2 }
Conclusion
The factor for deciding if an object in JavaScript is loopable is whether it has Symbol.iterator
inside. Only objects that have the Symbol.iterator
can be loopable— Array, typically.
So, objects such as { x: 1, y: 2 }
aren’t mappable, but there is a way to make it mappable. You can add a customized next()
method to them, which I don’t personally recommend — it’s too much work.