Lesson Sunday

In order to display all of the contacts in our application, we're going to need to loop through our address book's contacts. However, since our contacts are all stored in an object, we have to do this differently than we would with looping through an array.

In this lesson, we'll learn exactly how to do that. Then, in the next lesson, we'll apply what we've learned here to actually loop through our contacts.

In the process of learning about looping through object properties, we'll also do a deeper dive into prototypal inheritance - though not too deep!

Let's say we have an object that stores information about the mathematician Ada Lovelace in our address book application. (Don't add any of the following code to the address book application - we are just using this as an example.) We want to to take this information and convert it all into a single string which we'll display on our website.

Let's take a look at our object:

let mathematician = {
  firstName: "Ada",
  lastName: "Lovelace",
  profession: "Mathematician",
  funFact: "Daughter of Lord Byron",
  countryOfBirth: "England",
  yearOfBirth: 1815,
  yearOfDeath: 1852
}

We could just display each property individually (such as by doing mathematician.firstName) but that becomes less and less feasible - and results in more and more work - the more properties our objects have.

So let's turn it into one long string instead. In Address Book: Finding and Deleting Contacts, we learned about Object.keys(), a method that returns all the keys in an object. We can take advantage of this method to grab the keys in an array and then loop over them:

const adaKeys = Object.keys(mathematician);
let adaString = "";
adaKeys.forEach(function(key) {
  adaString = adaString.concat(key + ": " + mathematician[key] + "\n"); 
});

This isn't too bad - we create a constant called adaKeys that holds an array of the mathematician object's keys. Next we initialize an empty string called adaString.

Finally, we loop over our array of keys. For each key, we use String.prototype.concat() to add a stringified key and value along with a new line after each key-value pair.

The code above returns the following string:

firstName: Ada
lastName: Lovelace
profession: Mathematician
funFact: Daughter of Lord Byron
countryOfBirth: England
yearOfBirth: 1815
yearOfDeath: 1852

It may look the same as our object but now we have a string. This could be helpful if we wanted to append many different objects to the DOM, especially if they have different keys. There's no need to specify each key in our code. You might be thinking that the keys above don't look very pretty - they are formatted like JavaScript variables, not syntactically correct English. Well, our loop could also format the keys, automatically capitalizing the first letter and then using a regular expression to identify capital letters and then add separators so there is a space between each word. Only a little bit of code would be necessary to "prettify" this string so we could easily append entire objects to the DOM. We won't demonstrate how to do this in this lesson - after all, it has nothing to do with looping - but you may want to experiment with this in your own code!

In general, using Object.keys() is a very effective way to loop over properties in JavaScript. In fact, it's generally the best way to do so - and it's exactly what we'll do in the next lesson.

Now let's take a look at some syntactic sugar that JavaScript provides for looping through objects: the for...in loop. Caution: There is an important use case where we won't want to use for...in - we will cover it in a moment. Using Object.keys() will generally be better! Going over this gotcha will give us further insights into prototypal inheritance.

Here's an example of a for...in loop:

let adaString = "";
for (const key in mathematician) {
  adaString = adaString.concat(key + ": " + mathematician[key] + "\n");
}

The only thing we had to change here is the following syntax: for (const key in mathematician) - the statement itself (the code in {}) remains the same. As we can see, this is a special kind of for loop. We specify a variable name - here we call it key but we could call it property or something else. We then specify the object we are iterating over, which is mathematician.

This all seems great, right? Well, now for the gotcha - and it's a big one. Let's see what happens if we use for...in with a contact created using our Contact prototype:

function Contact(firstName, lastName, phoneNumber) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.phoneNumber = phoneNumber;
}

Contact.prototype.fullName = function() {
  return this.firstName + " " + this.lastName;
}

let contact = new Contact("Ada", "Lovelace", "111-111-1111");

for (const key in contact) {
  console.log(contact[key]);
}

Try running this code in the console and it will return the following:

Ada
Lovelace
111-111-1111
ƒ () {
  return this.firstName + " " + this.lastName;
}

The first three values make sense - but why is the following listed as a property?

ƒ () {
  return this.firstName + " " + this.lastName;
}

Well, let's take a closer look at the contact object in the console:

This object contains a "proto" property which in turn contains the "fullName" function.

In addition to the three properties we created, we'll see that Contact also has a __proto__ property. Both fullName and constructor are properties of this __proto__ object. This is how our humble contact object uses prototypal inheritance to get access to the Contact constructor and the fullName function. In turn, we can see that __proto__ also has a __proto__ property - which in turn contains the functionality of basic objects in JavaScript. If this is still confusing, don't worry - you do not need to fully grasp prototypal inheritance to get through this program. It's a concept that can take some time to fully understand.

When we call contact.fullName(), JavaScript will first look at our contact object to see if that method is attached to it. If it's not, it will look at __proto__ to see if the method is there. If it isn't, it will look at that __proto__'s __proto__ - until it finds the method. If it doesn't find the method, it will return Uncaught TypeError: [functionName] is not a function - where [functionName] is the name of the function it couldn't find.

Well, this is the problem with for...in - it doesn't just iterate over properties of an object - it iterates over all the enumerable properties of the object as well as enumerable properties in the prototype chain. (Enumerable just means that the property has an internal enumerable flag set to true.)

In the case of our contact object, that means for...in also enumerates over the properties of Contact - specifically Contact.prototype.fullName() - which we don't want!

This is actually a pretty annoying behavior - and it's too bad that for...in loops do this.

We can fix the issue by doing the following:

for (const key in contact) {
  if (contact.hasOwnProperty(key)) {
    console.log(contact[key]);
  }
}

The Object.prototype.hasOwnProperty() method returns a boolean. If a property belongs directly to an object (as firstName belongs to our contact object), it will return true. If the property doesn't belong to the object (as is the case of Contact.prototype.fullName(), which belongs to Contact), it will return false.

By the way, note that Object.prototype.hasOwnProperty() is called on Object - that's at the very top of the prototypal inheritance chain in JavaScript. If you go to the console and open the contact objects __proto__ and then open its __proto__, you'll see this method listed! Via prototypal inheritance, the contact object has access to Object.prototype.hasOwnProperty().

Well, so much for the syntactic sugar of a for...in loop. While we can verify that properties actually belong to objects, it's probably just better to iterate using Object.keys() instead. In fact, the Mozilla documentation mostly recommends for...in loops for debugging. Check out the docs for more information.

However, this dive into for...in hopefully provides some insight into how prototypal inheritance works. If you are feeling especially brave, you might even want to read more about Inheritance and the prototype chain. At this point, it's enough to have a very basic understanding of how JavaScript objects inherit from other objects.

In this lesson, we've learned a few ways to iterate over properties in an object. We've also learned a bit more about prototypal inheritance, a key and often very confusing topic for developers. In the next lesson, we'll use what we've learned to actually loop through the contacts in our address book application.

Terminology


  • __proto__: Objects have a __proto__ property which allows them to access other properties and functionality via prototypal inheritance.

  • Prototypal Inheritance: Inheriting functionality via object prototypes.

Examples


Loop Through Properties with Object.keys()

const adaKeys = Object.keys(mathematician);
let adaString = "";
adaKeys.forEach(function(key) {
  adaString = adaString.concat(key + ": " + mathematician[key] + "\n"); 
});

Loop Through Properties with for...in

for (const key in mathematician) {
  if (contact.hasOwnProperty(key)) {
    console.log(mathematician[key]);
  }
}

Note: Use Object.prototype.hasOwnProperty() if you only want the properties of the object itself to be iterated over.

Lesson 16 of 23
Last updated October 12, 2021