Object-Oriented JavaScript
OOP Basics: The Library Metaphor

Before you go any further in these tutorials, it's probably a good idea to stop and explain Object-Oriented Programming (OOP), why it's useful in general, and why understanding it better will make life easier when you dive into libraries and frameworks like the Google Maps API or jQuery.Wikipedia defines object-oriented programming as "a programming paradigm using "objects" - data structures consisting of data fields and methods together with their interactions". What this can be boiled down to is that OOP allows you to define types of variables that have certain properties and functions in common. For example, library books (a metaphor that we will rely on heavily in this tutorial) have titles and authors (properties) and can be checked in and out (functions). This is true of many library books, and therefore it makes sense to be able to write an 'object' that summarizes what a library book can do (or what can be done to it), and how it interacts with a library (which is another class of object). That way, any time I create a variable of type 'book' in my JavaScript, I can assume it will automatically have certain properties and be able to call certain functions. Since I've already mentioned the library example, let's start building a library that has books. I'll start with non-OOP code and gradually re-factor it as we progress, so that you can see the benefits of OOP before your very eyes.

Establishing our Specifications

When you're about to code a project, it's a good idea to get a clear idea of what your specifications will be. What will your application need to be able to do? What kind of information will you manage? What input will you expect from users, and how will they interact with the application? On a more basic level (for example, here we won't be doing any user interaction yet... just defining a library and writing some test scripts to demonstrate the functionality of our object classes), you have to imagine what concepts you're dealing with, and what the properties and capabilities of those concepts should be. Let's lay out a few expectations for our library:

  • Libraries will have a name, latitude and longitude, and a collection of books. Each book will be an instance of the type 'Book', defined in the next bullet point. Libraries should be able to acquire books, as well as check them in and out.
  • Books will have a title, an author, and a status such as checked in or out. Of course books have more information than this, but for now this will suffice.

That's a good set of initial specifications to work with. We can expand the concept and functionality our library metaphor as we proceed. So it looks like we need to have two types of objects so far: Library and Book. Moreover, it's interesting to note that any Library that we create will have an array that contains zero or more objects of type Book. With that in mind, let's start defining our classes.

Defining Classes

In JavaScript, classes are defined as functions. Therefore, a class definition is going to look a lot like a function with parameters when you build it. The following code defines a Book class:

var Book = function(title, author, status) { if (!status) { status = "Checked In"; } this.title = title; this.author = author; this.status = status; };

Keep in mind that these parameters can be optional, as long as you handle scenarios in which they're not passed in within your function block. It's also possible to leave the parameters blank and then just set properties on the object after it has been created, however it is frequently the case that you want to be able to create a new instance of an object and set a few initial values in one fell swoop. It's the difference between...

var myBook = new Book("The Title", "The Author", "The Status");

... and ...

var myBook = new Book(); myBook.title = "The Title"; myBook.author = "The Author"; myBook.status = "The Status";

Once we've declared the function and established any possible parameters, we want to start fleshing out the function body, which will serve two main purposes: (1) establishing properties that the object of that type will have, including setting any that are dependent on what is passed in through parameters), and (2) defining and methods: the functions that the object can perform.

In our example, our book won't really perform a function to speak of, so we don't have any additional function definitions within the body of the Book() class. Therefore, inside of the function, all we really have to do is establish the properties of the object. This is done using the 'this' keyword. You may remember that 'this' refers to the element that calls the function when you add an event listener to an HTML element. So clicking a button with onclick="alert(this);" inside of its tag will pop up something like [object HTMLButtonElement] when you click it. Along these lines, the 'this' inside of a class definition will always refer to the current object instance that you're dealing with. So to set properties that are specific to that particular instance of the object, simply grab each variable from the parameter and then assign it to a similarly-named variable attached to the 'this' reference:

this.title = title; this.author = author; this.status = status;

You may also have noticed that, before setting these values, I performed the following check:

if (!status) { status = "Checked In"; }

This line checks to see if 'status' was passed in, and if not, sets it to "Checked In" as the default. This is a good way to set default values in case you don't always expect all parameters to be passed. What this means is that I can create an instance of a book as I did with "White Fang" in the example above without necessarily providing all three parameters. (However I did not do this with title or author, which means failing to pass in the first two parameters would cause an error.)

Defining Object Methods

Objects have properties. For example, a car has a certain number of doors, horsepower, paint color, etc. These values may vary, but they're properties that all (or most) cars have on some basic level. Similarly, objects typically do similar things. For example, cars lock and unlock, start up, drive, etc. So it's useful for object classes to also be able to describe what objects of that type can do. This is possible by defining object methods. We've created a Book class, and now I'd like to create our Library class, and include in it some methods for acquiring books and checking them in and out. Let's look at how we can add a method to our class to acquire books to begin with:

var Library = function(name, latitude, longitude) { this.name = name; this.latitude = latitude; this.longitude = longitude; this.books = []; this.acquire = function(book) { this.books.push(book); } };

Note how the method was defined. Just like properties, I used the "this" keyword, and then gave a name to the function as if it were a variable. Then I proceeded to set it equal to a function definition that takes one parameter. Inside of that function definition, I had access to two variables: 'this' and 'book'. The 'this' parameter will always refer to the library that is calling the acquire() function, and the 'book' parameter will apply to whatever book I pass in. If the book exists elsewhere in my code, then JavaScript will treat it as a reference to that book rather than the creation of a new book. This is handy in case you already have a collection of books created elsewhere. We'll dive deeper into that issue as we progress into more advanced issues of scope and reference. For now, suffice it say that the 'acquire' method takes a book that is passed in and adds it to the array of books in that library's collection.

... And that's it! We've defined our first method. It's a fairly simple one, and as we'll see in the next section, might even be considered useless if the objects aren't fleshed out to be more complicated, but hopefully it serves the purpose of demonstrating how to create an object with properties and then define functions that it can carry out. Let's move on to working with objects that we create.

Working with an Object

Having established our Library() and Book() classes, let's create a library, add some books to it, and run some code to show what we've got:

var library = new Library("MCPL", 11.111, 22.222); library.acquire(new Book("Great Expectations", "Charles Dickens", "Checked Out")); library.acquire(new Book("White Fang", "Jack London")); var firstObjectOutput = document.getElementById('firstObjectOutput'); firstObjectOutput.innerHTML = library.name + " has " + library.books.length + " books:
"; for (var i in library.books) { firstObjectOutput.innerHTML += " - " + library.books[i].title + ", written by " + library.books[i].author + " is " + library.books[i].status + ".
"; }

Quick! Note how I looped through the books: Instead of the usual for(i=0;...;i++) loop, I simply said "for(var i in books)". This will do essentially the same thing. It's easier to type quickly, and is useful when you don't need to know which numbered index you're currently at. It will simply loop through all the indexes it finds. This is ESPECIALLY useful later if you are dealing with an array that uses text indexes instead of incremented numbers.


Recommended next: Scope: IIFEs and Closure