The for...in Loop and Enumerable Properties

Published: Monday, July 18, 2022
Updated: Tuesday, July 19, 2022

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.

javascript
Copied! ⭐️
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.

javascript
Copied! ⭐️
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.

javascript
Copied! ⭐️
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.

javascript
Copied! ⭐️
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.

javascript
Copied! ⭐️
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.

javascript
Copied! ⭐️
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.

javascript
Copied! ⭐️
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!

Resources