Lesson Weekend

In a real world application, we'd save our address book's Contacts in a database. However, we aren't working with databases yet. Instead, we'll create a "mock" database (a fake database) and store its data inside a global variable.

As we discussed in Variable Scope, we want to avoid global variables wherever possible. So why are we going to use one here?

Well, one of the biggest problems with global variables is that they never fall out of scope - and their values persist throughout an application. Generally, this is a recipe for bugs. However, we want the values in a database to persist and be available all throughout an application. What is the point of a database if we can't retrieve data from it? That's why we're using a global variable here - to better imitate what a database actually does.

Keep in mind also that our mock database wouldn't actually be useful in the real world so we wouldn't use a global variable like this in a real world application, either. At this point, you might wonder why we don't just jump into using databases then. Well, they're pretty complicated! For now, we will stay focused on core JavaScript concepts. It will still be a while before we start working with actual databases.

Also, just because we are using a global variable to mock a database doesn't mean you should start adding global variables throughout your code. For the next several sections, here is a general rule of thumb: if your variable is meant to represent a potential database, a global variable is fine. Otherwise, avoid them if possible. Most of the projects we do throughout Intermediate JavaScript will not need to mock a database, so think very carefully about whether or not you need to add this functionality as you build your projects.

AddressBook Constructor

Much like our Contacts, our AddressBook will be a JavaScript object. But instead of containing properties like firstName or lastName, it will contain a list of Contact objects, similar to the manner the previous lesson depicted objects being saved within other objects.

To do this we'll need an AddressBook constructor. Let's add the following new constructor to the top of scripts.js:

scripts.js
function AddressBook() {
  this.contacts = {};
}

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

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

AddressBook objects contain a single property: An empty object called contacts. This is where we'll store entries in our address book. Each entry will be a Contact object. As we can see, we'll be storing objects with an object - all of the Contact objects will be stored in a single AddressBook object. If we wanted to, we could build out our application to have many instances of AddressBooks, each with their own Contacts. However, we will keep this simple and just create one instance of AddressBook.

We'll also add comments showing where AddressBook and Contact logic will go in scripts.js. This will make it easier to follow along with the lessons.

scripts.js
// Business Logic for AddressBook ---------
function AddressBook() {
  this.contacts = {};
}

// Business Logic for Contacts ---------
function Contact(firstName, lastName, phoneNumber) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.phoneNumber = phoneNumber;
}

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

AddressBook Prototypes

AddressBooks can only do one thing right now: store a list of contacts in key-value pairs. Let's define a few prototypes for our AddressBook objects to give them more functionality.

Adding Contacts to the AddressBook

We'll create a prototype method to add new Contacts to an AddressBook. This will go right below the AddressBook constructor:

scripts.js
// Business Logic for AddressBook ---------
function AddressBook() {
  this.contacts = {};
}

AddressBook.prototype.addContact = function(contact) {
  this.contacts[contact.firstName] = contact;
};

// Business Logic for Contacts ---------
function Contact(firstName, lastName, phoneNumber) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.phoneNumber = phoneNumber;
}

Contact.prototype.fullName = function() {
  return this.firstName + " " + this.lastName;
};
  • Our new AddressBook.prototype.addContact() method takes a Contact object as an argument.

  • this.contacts is the object where we're storing all of our Contact objects. Generally, a contact in a real database will have an ID. We'll add numerical IDs soon. For now, we'll use a Contact's firstName property as an ID. This ID will be the key (this.contacts[contact.firstName]) while the contact itself is the value (contact`). In other words, we are assigning a new value to a new key with the following line:

this.contacts[contact.firstName] = contact;

That's all it takes for us to add a new Contact object to our AddressBook!

Let's try it out. We can copy/paste the contents of scripts.js into the JavaScript console, and enter each of the following five lines:

let addressBook = new AddressBook();
let contact = new Contact("Ada", "Lovelace", "503-555-0100");
let contact2 = new Contact("Grace", "Hopper", "503-555-0199");
addressBook.addContact(contact);
addressBook.addContact(contact2);

Let's walk through what each of these lines is doing:

  1. We create an AddressBook object.
  2. We create a new Contact object with a firstName of "Ada", under the variable name contact.
  3. We create another new Contact object, this time with a firstName of "Grace", under the variable name contact2.
  4. We add the first Contact object to our AddressBook, using our new addContact() method.
  5. We add the second Contact object to the AddressBook using the same new method.

Viewing Contacts in the AddressBook

If we then run the following in the console, we can see the contents of our AddressBook:

addressBook;

It will return something that looks like this:

AddressBook {contacts: {…}}

We can see that the addressBook is an object that contains another object called contacts. To access these contacts, we can do the following:

addressBook.contacts;

This will return the following:

{Ada: Contact, Grace: Contact}

Both of our contacts are there! But how do we access them? Well, each object has a key. The first one has a key of Ada while the second has a key of Grace. So we can access them like this:

addressBook.contacts["Ada"];

This returns the following:

Contact {firstName: "Ada", lastName: "Lovelace", phoneNumber: "503-555-0100"}

We can do the same for addressBook.contacts["Grace"].

Note that we cannot do the following:

addressBook.contacts[Ada];

We'll get the following error if we do:

Uncaught ReferenceError: Ada is not defined

This is because JavaScript is reading this as a variable, not a string - and we haven't defined an Ada variable. Instead, if the key is a string, we need to write it as a string.

If we wanted to get even more specific information about Ada - for instance, her phone number - we can do so:

> addressBook.contacts["Ada"].phoneNumber;
"503-555-0100"

We just need to identify the property we want the value of - in this case, it's the phoneNumber property. Sometimes objects can be very deeply nested. No matter how deeply nested an object or property is, we can keep drilling down further until we retrieve it. We will cover this further in a future lesson.

Before we continue, you might be wondering why we store all the contacts in this.contacts. Why not store it in the AddressBook object itself instead of as a property of the AddressBook? Well, we could technically do that. However, what if we want our AddressBook to have other properties? For instance, we might want a property called owner which gives information about the owner of the AddressBook. Or we might want a lastModified time stamp that tells us when the AddressBook was last modified. In the next lesson, we'll actually add a property to help us assign IDs to each contact.


Example GitHub Repo for the Address Book

Examples


Add contacts to an AddressBook with its constructor and prototype methods:

scripts.js
// Business Logic for AddressBook ---------
function AddressBook() {
  this.contacts = {};
}

AddressBook.prototype.addContact = function(contact) {
  this.contacts[contact.firstName] = contact;
};

// Business Logic for Contacts ---------
function Contact(firstName, lastName, phoneNumber) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.phoneNumber = phoneNumber;
}

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

Example GitHub Repo for the Address Book

Lesson 8 of 32
Last updated April 8, 2021