In a real world application, we'd save our address book's Contact
s 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
ConstructorMuch like our Contact
s, 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
:
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 AddressBook
s, each with their own Contact
s. 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.
// 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
PrototypesAddressBook
s 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.
Contact
s to the AddressBook
We'll create a prototype method to add new Contact
s to an AddressBook
. This will go right below the AddressBook
constructor:
// 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:
AddressBook
object.Contact
object with a firstName
of "Ada"
, under the variable name contact
.Contact
object, this time with a firstName
of "Grace"
, under the variable name contact2
.Contact
object to our AddressBook
, using our new addContact()
method.Contact
object to the AddressBook
using the same new method.Contact
s 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.
Add contacts to an AddressBook
with its constructor and prototype methods:
// 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;
};
Lesson 8 of 32
Last updated April 8, 2021