JavaScript for...in vs for...of Loops

To best understand the for..in and the for...of statements, we need to talk about enumerable properties and iterable objects from a high level.

Enumerable Properties and for...in Statements

Object properties are almost always seen as key/value pairs, but these properties also have attributes associated with them.

const house = {
  cost: 300000,
  rooms: 2,
  baths: 2,
};

console.log(Object.getOwnPropertyDescriptor(house, 'cost'));

// {
//   configurable: true
//   enumerable: true
//   value: 300000
//   writable: true
// }

In the above example, we are using the utility Object.getOwnPropertyDescriptor() to see the house object's cost property attributes.

We see that the cost property has an attribute called enumerable that is set to true. Is that what makes a property be enumerable? For the sake of JavaScript, yes, this is exactly what makes a property enumerable.

What's so special about properties that are enumerable? Enumerable properties show up in for...in loops (unless that property happens to be a Symbol). By default, all properties added to objects through normal assignment have their enumerable property set to true.

const apartment = {
  rent: 1000,
  rooms: 2,
  baths: 1,
};

apartment.sqft = 950;

Object.defineProperty(apartment, 'floorPlan', {
  value: 'A',
  enumerable: false,
});

for (let prop in apartment) {
  console.log(prop);
}

// rent
// rooms
// baths
// sqft

In this example, we have an object called apartment that has some properties assigned a couple of different ways. We added properties directly within the object, and also via assignment.

We then went ahead and added a property called floorPlan using the Object.defineProperty() method. Adding a property this way allows us to change the attributes associated with that property. In this case, we wanted to explicitly change enumerable to be false.

What happens to this property when we run our for...in loop? It doesn't show up! As I mentioned before, in JavaScript, only if the enumerable property discriptor is set to true, will that property be enumerable.

The for...in statement only loops over properties that are enumerable.

In this case, floorPlan is not enumerable, so the for...in loop ignores it.

One more thing to note about the for...in statement is that it loops over properties, not its values.

Iterable Objects and for...of Statements

Iterable objects are objects that provide an interface that defines its iteration behavior over that object's values. A technical definition for this is that an object becomes iterable (“implements” the interface Iterable) if it has a method (own or inherited) whose key is Symbol.iterator. I'll go into a deeper explanation of this in another blog post.

For the most part, most built-in objects in JavaScript are iterable objects. Plain objects (as created by object literals), however, are not iterable.

How does for...of fit in with all of this? for...of is a loop that only works with iterable objects.

The for...of statement loops over iterable values that are defined to be returned by its iteration behavior.

Arrays

The most common iterable in JavaScript is the array. Its iteration behavior is pretty simple to define; it sequentially iterates over its elements. We can use a for...of loop to test this out.

const belongings = [
  'computer',
  'home',
  'car',
]

for (let item of belongings) {
  console.log(item);
}

// computer
// home
// car

The Array object's iterator is responsible for giving us back each value defined in this array.

What would happen if we loop over belongings with a for...in loop instead, and how does this differ from a for...of loop?

for (let prop in belongings) {
  console.log(prop);
}

// 0
// 1
// 2

If you recall from above, by default, all elements defined in this array have their properties, 0, 1, and 2, set with their attribute enumerable equal to true. This means we get back all its enumerable properties.

The for...of loop, however, only returns back the values which are iterable.

An easy way to visualize this is by adding another property to this Array object using an assignment, and the Object.defineProperty() method.

belongings.his = 'guitar';

Object.defineProperty(belongings, 'hers', {
  value: 'phone',
  enumerable: false,
});


for (let item of belongings) {
  console.log(item);
}

// computer
// home
// car


for (let prop in belongings) {
  console.log(prop);
}

// 0
// 1
// 2
// his

for...of again only returned the direct values in the array (as defined in its iteration behavior). for...in returned only those properties that have their enumerable attribute set to true. This is why we don't see the property hers outputed.

Recap

Enumerable in JavaScript means properties whose enumerable property descriptor is set to true; These properties are what get returned to us in a for...in loop.

Iterable objects are objects that define their own iteration behavior over that object's values; These are the values that get returned to us in a for...of loop.