The for...in Loop and Enumerable Properties
Greetings, friends! Enumerable properties and for...in loops in JavaScript can seem confusing for beginners. In this tutorial, I'll discuss what for...in
loops do and how the JavaScript engine determines what properties to iterate over.
What are for...in Loops?
The for...in
statement let us iterate over all enumerable properties of an object. Let's look at an example of using a for...in
loop with a simple object.
const object = {a: 1, b: 2, c: 3};
for (const key in object) {
console.log(key);
}
/* OUTPUT:
a
b
c
*/
The code above will iterate through every property key in object
and log them to the console. We can also get the value of each property in the object as shown in the code below.
const object = {a: 1, b: 2, c: 3};
for (const key in object) {
console.log(object[key]);
}
/* OUTPUT:
1
2
3
*/
The for...in Loop with Symbols
If we decided to add new properties to an object with keys that are Symbols instead of strings, let's see what happens.
const object = {
a: 1,
b: 2,
c: 3,
[Symbol('d')]: 4,
[Symbol('e')]: 5,
};
for (const key in object) {
console.log(key);
}
/* OUTPUT:
a
b
c
*/
for (const key in object) {
console.log(object[key]);
}
/* OUTPUT:
1
2
3
*/
After running the code above, we can see that Symbols are completely ignored by for...in
loops!
What are Enumerable Properties?
We have seen that the for...in
loop can magically iterate over properties in an object, but how does the JavaScript engine decide what properties are considered enumerable? It turns out that every property on an object has special attributes called property descriptors. One of these property descriptors is called enumerable
.
Enumerable properties are properties of an object whose internal enumerable
flag (property descriptor) is set to true
, which is the default for properties created via simple assignment or via a property initializer. When we create an object by assigning it to a variable similar to what we've been doing throughout this tutorial, each property will have the enumerable
descriptor set to true
.
We can actually set the enumerable
descriptor to false
to hide properties during a for...in
loop by using Object.defineProperty or Object.defineProperties.
const object = {
a: 1,
b: 2,
c: 3,
hideMePlz: 4,
};
Object.defineProperty(object, 'hideMePlz', {
enumerable: false
});
for (const key in object) {
console.log(key);
}
/* OUTPUT:
a
b
c
*/
When we run the code, the hideMePlz
property will be hidden from the for...in
loop! If we log the object to the console, we can see that the property still exists on the object. It's only hidden when using a for...in
loop.
console.log(object);
/* OUTPUT
{
a: 1,
b: 2,
c: 3,
hideMePlz: 4
}
*/
If you logged object
to the console in Google Chrome, then you may notice that the hideMePlz
property is in a faded color compared to the other properties. I'm using dark mode, so it appears as a dark shade of blue. This indicates that the property isn't enumerable anymore.
Fun fact: did you know that arrays have a length
property that has an enumerable
property descriptor set to false
? As indicated by the faded color in Google Chrome, this appears to be the case.
Which Properties are Enumerable?
If you need help determining whether a property on an object is considered enumerable, you can use the Object.propertyIsEnumerable method.
const object = {
a: 1,
b: 2,
c: 3,
hideMePlz: 4,
};
console.log(object.propertyIsEnumerable('hideMePlz')); // true
Object.defineProperty(object, 'hideMePlz', {
enumerable: false
});
console.log(object.propertyIsEnumerable('hideMePlz')); // false
If we want to look at the enumerable
property for all properties on an object instead of just one, then we can use the Object.getOwnPropertyDescriptors method.
const object = {
a: 1,
b: 2,
c: 3,
hideMePlz: 4,
};
Object.defineProperty(object, 'hideMePlz', {
enumerable: false
});
const descriptors = Object.getOwnPropertyDescriptors(object);
for (const key in descriptors) {
console.log(key + ': ' + descriptors[key].enumerable);
}
/* OUTPUT:
a: true
b: true
c: true
hideMePlz: false
*/
The Object.getOwnPropertyDescriptors
method will return a new object that contains information about the property descriptors for each property on our original object. We're using a for...in
loop on descriptors
to find out what properties are considered enumerable
.
In case you're wondering how we're able to use a for...in
loop to detect enumerable properties that are false
, keep in mind that we're creating a new object called descriptors
. As mentioned earlier in this tutorial, assigning a new object to a variable will set the enumerable
descriptor to true
by default.
Enumerable vs Iterable
Although they sound similar, enumerability and iterability are not the same. Objects are considered enumerable when they contain properties with the enumerable
property descriptor set to true
.
Objects are considered iterable when they implement the iterable protocol. For an object to implement the iterable protocol, it must contain a method named Symbol.iterator
that returns an iterator. You can learn more about iterables and iterators in my tutorial series that discusses them.
Conclusion
In this tutorial, we have learned how to use for...in
loops and what enumerable properties are. In the next tutorial, I will discuss the differences between for...in
and for...of
loops and when to use each one!