View Screencast | Download code

JavaScript does not support classes. At least, not in the traditional sense. It does support basic class features, such as inheritance and method overrides. However, it does all of this though functions and function prototypes, making the implementation of class features is a bit more complicated.

JavaScript works well with object instances. There are basically two ways to create an object in JavaScript. First, you can do it directly, which creates an empty object. And second, you can use a constructor function.

Let’s look at creating an empty object directly. Creating the empty object can be done one of two ways (the latter being preferred):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// The old way...
var jane = new Object();
 
jane.firstName = 'Jane';
jane.lastName = 'Doe';
jane.getFullName = function() {
  return this.firstName + ' ' + this.lastName;
}
 
// This way is preferred...
var joe = {};
 
joe.firstName = 'Joe';
joe.lastName = 'Blow';
joe.getFullName = function() {
  return this.firstName + ' ' + this.lastName;
}

This creates two empty objects, jane and joe. Then it adds the properties firstName and lastName, and the method getFullName to both of these objects. Unlike traditional classes, where you can’t add properties and methods to an instance of the class, JavaScript objects are mutable, meaning we can change them after they’ve been created.

The drawback to this method is that if we want to create a third object with the same properties and methods as first two, we have to duplicate the code to add these properties and methods to the third object after its been created. One way around this is to create a function that we can run each new object through that will add those properties and methods, like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
function initPersonObject(obj, first, last) {
  obj.firstName = first;
  obj.lastName = last;
 
  obj.getFullName = function() {
    return this.firstName + ' ' + this.lastName;
  }
}
 
var jane = { };
initPersonObject(jane, 'Jane', 'Doe');
 
var joe = { };
initPersonObject(joe, 'Joe', 'Blow');

The code above might seem a bit confusing. Look at the getFullName function, and note that it uses the variable this. What does this refer to? From the code it might not be immediately obvious, but remember that we are adding the getFullName method to the obj that was passed in. So when we actually execute the getFullName function, this will refer to the object that getFullName belongs to.

There are, of course, other variations on this theme. We could create the empty object inside the initPersonObject function, then return it from the function. We could then create our object in one line of code. This would be similar to a factory design pattern. I’ll leave it up to you to experiment with that if you’d like.

The second way to create an object instance is through the use of a constructor function, which streamlines the above code and makes it more readable. A constructor function is not a class, though it is often confused for a class. The constructor function is how we can create objects of the same type:

1
2
3
4
5
6
7
8
9
10
11
function constructPerson(first, last) {
  this.firstName = first;
  this.lastName = last;
 
  this.getFullName = function() {
    return this.firstName + ' ' + this.lastName;
  }
}
 
var jane = new constructPerson('Jane', 'Doe');
var joe = new constructPerson('Joe', 'Blow');

This doesn’t look much different that out initPersonObject from before, except that we are not passing in the obj to the constructor function, and we are always using this to refer to the object instance. The constructor function, in combination with the new keyword, allows us to create objects and initialize them at the same time.

Here’s how it works. When JavaScript encounters the new keyword, it creates a new empty object, just like what we did in the examples above. Next, it executes the constructor function and sets the this variable of that function to point to the new object. The constructor function then adds properties and methods to the new object using the this variable. When the constructor function ends, we are left with an object that has a complete set of properties and methods, just like what we had at the end of our initPersonObject example. Finally, the new keyword takes the newly constructed object and returns it. In this example, the new object is assigned to a variable.

With this method we have something similar to a class. We can now create different objects of the same type. One drawback to using this type of constructor function is that the methods we add are attached the object instance. In other words, each time we create an object instance, we are creating a new instance of the method getFullName. The function is duplicated for each object. This is silly, because the code is the same for each object. When the function is executed it should simply point to the specific object instance that it is running against. This is how traditional classes work.

JavaScript does provide a solution for this. Each constructor has a prototype property. The prototype property is an object that contains methods to be shared between object instances. When a new object instance is created, pointers to the prototype methods are created. When a prototype method is executed, the this variable in that method is set to the object instance on which the method is called. The result is that the same code is executed against a different object.

1
2
3
4
5
6
7
8
9
10
11
function constructPerson(first, last) {
  this.firstName = first;
  this.lastName = last;
}
 
constructPerson.prototype.getFullName = function() {
  return this.firstName + ' ' + this.lastName;
}
 
var jane = new constructPerson('Jane', 'Doe');
var joe = new constructPerson('Joe', 'Blow');

In this example, calling jane.getFullName() and joe.getFullName() both execute the same shared function, but in the former the this variable will point to jane, and in the latter the this variable will point to joe. In both cases, when the getFullName method is called, JavaScript first checks the object instance to see if the method exists (the method would exist in the object instance if it was added inside the constructor or after the object was created, like in our very earlier code examples). If the method does not exist in the object, JavaScript then checks the prototype of the constructor function that was used to create the object. Since the method is found there, the this variable is set to the specific object instance and the method is executed.

It’s important to understand this process. A constructor function is not a class. It is simply a function that gets executed against an object, and its prototype is referenced from that object. Notice that in all these examples I named the function “constructPerson”. I did that deliberately. One reason people get confused is that they see code where the constructor function is named like a class.

1
2
3
4
5
6
7
8
9
10
function Person(first, last) {
  this.firstName = first;
  this.lastName = last;
}
 
Person.prototype.getFullName = function() {
  return this.firstName + ' ' + this.lastName;
}
 
var jane = new Person('Jane', 'Doe');

Notice that the constructor function is now called “Person”, which implies that this is a class. It’s not. It’s a constructor function. If you use the typeof operator on the Person constructor, you will find that it returns “function”, not “class”. If you use the typeof operator on an object instance created with the Person constructor, it will return “object”, not “Person”. There is an instanceof operator in JavaScript that we can use to check if an object is an instance created with a particular constructor, and we’ll take a look at that in more detail in the next post about inheritance. But there is never any reference to a “class” anywhere in JavaScript (though that may change in a future implementation of the language. Someday. Maybe.)

At the start of this post, I mentioned that JavaScript supports inheritance and method overrides. We’ll take a look at inheritance next, and we’ll see that its going to take some additional code to get JavaScript inheritance to work like traditional class inheritance.