View Screencast | Download code

The JavaScript programming language does support extending classes through the use of constructors and prototypes. As you’ll see in the next series of posts, it is more complicated to implement than using a simple “extends” keyword or similar construct like other programming languages. This is especially true if we want to use method overrides. In this post I’m going to focus on making properties in a base class available in descendant classes, and I’ll focus on doing the same with methods in subsequent posts. For this example, I’m going to expand the original Person example from the previous post. I’m going to create a class hierarchy that looks like this:

Figure 1 - Class Diagram

Figure 1 - Class Diagram

The first JavaScript class, Person, will need to have properties for the person’s first and last names, and a method to get the person’s full name by concatenating the first and last names. These requirements are handled in the test case for the Person class. (This is not a thorough test, since all I’m really testing is the inheritance model and not that properties and functions handle all the various types of inputs.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var Assert = YAHOO.util.Assert;
 
var testCaseInheritance = new YAHOO.tool.TestCase({ 
 
  name: "Test Inheritance", 
 
  testPerson: function() {
    var jane = new Person('Jane', 'Doe');
 
    Assert.isNotNull(jane, 'A');
    Assert.areSame('Jane', jane.firstName, 'B');
    Assert.areSame('Doe', jane.lastName, 'C');
    Assert.areSame('Jane Doe', jane.getFullName(), 'D');
  }
 
});

Each of these classes will be represented in its own JavaScript file using a constructor function and function prototypes. Here is the Person.js file that contains the Person constructor:

1
2
3
4
5
6
7
8
9
10
11
/**
 * @class Person
 */
function Person(first, last) {
  this.firstName = first;
  this.lastName = last;
};
 
Person.prototype.getFullName = function() {
  return this.firstName + ' ' + this.lastName;
};

This is a simple constructor, and it passes the test. Now, I want to create a new class, Employee, that inherits from Person. This class will need to add a property for the company that the person works for. It will also have a method that takes the company name and returns it as a web address. (Of course, we wouldn’t do this in real life. This is just for illustrative purposes!)

So the first thing I am going to do is create my test to verify that these new requirements work correctly. So, I add to testCaseInheritance in the tests.js file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
testEmployee: function() {
  var jane = new Employee('Jane', 'Doe', 'CoolCo');
 
  Assert.isNotNull(jane, 'A');
 
  // Testing Employee
  Assert.areSame('CoolCo', jane.company, 'E');
  Assert.areSame('http://www.CoolCo.com', jane.getWebSite(), 'F');
 
  // Testing inherited from Person
  Assert.areSame('Jane', jane.firstName, 'B');
  Assert.areSame('Doe', jane.lastName, 'C');
  Assert.areSame('Jane Doe', jane.getFullName(), 'D');
}

After running this test to verify it fails, I then I create my new Employee constructor.

1
2
3
4
5
6
7
8
9
10
11
/**
 * @class Employee
 * @inherits Person
 */
function Employee(first, last, company) {
  this.company = company;
};
 
Employee.prototype.getCompanyWebsite = function() {
  return 'http://www.' + this.company + '.com';
};

At this point, there is no code to implement inheritance, so my tests still fail. Let’s look at the properties first. How can I get the Employee constructor to inherit the properties of the Person constructor? If I think back to how JavaScript handles objects when executing constructor functions, I might just get an idea for how to do that. If you recall, when we execute a constructor using the new keyword, JavaScript creates a new, empty object, assigns it to the this variable in the constructor function, then executes the constructor function. In the Employee constructor, I will have a this variable pointing to a new, empty object. I should be able to pass that object to the Person constructor function, and have it initialized with all the properties of the Person class.

The question is, how can I do that? I need to pass the object to the Person constructor, but I don’t want to add a new argument to the constructor function. Fortunately, JavaScript has two handy methods: apply and call. Learn these well, as you will find them useful on many occasions working with JavaScript objects.

These functions are actually methods of JavaScript functions. That is, if you have a function such as showMessage(), then this function will have the methods showMessage.apply() and showMessage.call(). Remember, functions in JavaScript are objects, so they can have properties and methods!

Both of these functions take, as their first argument, an object that you want your function to be executed against. More specifically, apply and call will execute the function (showMessage in this example) and set the this variable to the object that was passed in as the first argument. Here’s an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// A message class with a msg property
function Message(msg) {
  this.msg = msg;
}
 
// A function to show a message, but this function does not
// have a msg property and it is not part of the Message class.
function showMessage() {
  alert('You say: ' + this.msg);
}
 
// Create a Message object.  The msg property is set to "Hello".
var m = new Message('Hello');
 
// Call the showMessage function, but set the "this" variable to 
// object "m" (passed in as first argument).
showMessage.call(m);
 
// Alerts: "You say: Hello"

Study this example a bit, and make sure you understand how the call function works. The apply function works the same way. The difference between the two is the other arguments that they take, which I’ll explain as we get back to our Person and Employee classes.

So, I should be able to edit my Employee constructor so that it calls the Person constructor, and sets the this variable in the Person constructor to the empty Employee object. This will initialize the Employee object with the properties for the Person class.

1
2
3
4
function Employee(first, last, company) {
  Person.apply(this);    // "this" is our new, empty object.
  this.company = company;
};

Line 2 does the magic. Remember, Person is not a class. It is a constructor function. Therefore, it has the call and apply methods. Now if I run my test… it still fails.

An examination of the error shows that the firstName and lastName properties are not being initialized properly. Since I have called the Person constructor, the Employee object now has firstName and lastName properties, but the values of the first and last arguments are not being passed to the Person constructor. I could set those properties in my Employee constructor, where those arguments are being passed in. However, this makes calling the Person constructor pointless, since I initializing the properties will create them if they don’t already exist. In other words, I don’t need the Person constructor to create the properties. But what I really want is for the Person constructor to create and initialize the properties itself. This way, I can create multiple classes that inherit from the Person class without having to explicitly initialize the properties in each descendant class. Otherwise, I would be defeating the purpose of inheritance!

Fortunately, the call and apply methods allow me to also pass arguments to the function that I am executing. And this is the difference between the two: the call function takes a variable number of arguments (after the first argument), and each of these is passed in to the function we are executing. The apply function takes a single second argument that is an array containing the arguments to be passed in to the function we are executing.

So, I could do the following:

1
2
3
4
function Employee(first, last, company) {
  Person.apply(this, arguments);
  this.company = company;
};

Each JavaScript function has an arguments variable that is an array containing all of the arguments that have been passed in to the function. (Actually, arguments is array-like. That is, it is not really a JavaScript array. However, for our purposes in this example, we can treat it like an array.) So in this example, arguments will contain three elements corresponding to the three arguments passed into the Employee constructor: first, last and company. While this works, it does have a couple drawbacks. First, the Person constructor does not need the value for company. Second, if we add a third constructor argument to the Person constructor, say a middle name, it will be set to the value for company unless we explicitly change the Employee constructor. Third, the order of the arguments in Person and Employee must always match, which again could cause problems if we ever want to change either one.

So, a better approach would be to use the call function, like so:

1
2
3
4
function Employee(first, last, company) {
  Person.call(this, first, last);
  this.company = company;
};

This will allow us to pass in only the specific arguments we need in the specific order we need them to be in. Simply put, it gives us more control. Now if I run my test… it still fails. The properties now pass OK, but the method getFullName fails because it has not be inherited by the Employee constructor from the Person constructor.

Before I look into inheriting the methods, however, I should make sure that constructors can be chained together. That is, I should test a descendant of Employee to make sure that it still inherits properties successfully from the Person constructor and from the Employee constructor. I am going to add two new tests for this, one for the Manager class and one for the Worker class.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
testManager: function() {
  var jane = new Manager('Jane', 'Doe', 'CoolCo');
 
  Assert.isNotNull(jane, 'A');
 
  // Testing Manager
  Assert.isArray(jane.workers, 'G');
 
  // Testing inherited from Employee
  Assert.areSame('CoolCo', jane.company, 'E');
  Assert.areSame('http://www.CoolCo.com', jane.getWebSite(), 'F');
 
  // Testing inherited from Person
  Assert.areSame('Jane', jane.firstName, 'B');
  Assert.areSame('Doe', jane.lastName, 'C');
  Assert.areSame('Jane Doe', jane.getFullName(), 'D');
},
 
testWorker: function() {
  var jane = new Worker('Jane', 'Doe', 'CoolCo');
 
  Assert.isNotNull(jane, 'A');
 
  // Testing Manager
  Assert.isNull(jane.manager, 'G');
 
  // Testing inherited Employee
  Assert.areSame('CoolCo', jane.company, 'E');
  Assert.areSame('http://www.CoolCo.com', jane.getWebSite(), 'F');
 
  // testing inherited Person
  Assert.areSame('Jane', jane.firstName, 'B');
  Assert.areSame('Doe', jane.lastName, 'C');
  Assert.areSame('Jane Doe', jane.getFullName(), 'D');
}

Note the order of the assertions in this test. It is important to remember that if a single assertion fails, that particular test ends. So, if I tried to assert on the getFullName method before the asserting on the other properties like workers, manager and company, then the test would never get to those assertions.

Testing these to ensure they fail, I then create the constructor functions to implement each of these classes:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
 * @class Manager
 * @inherits Employee
 */
function Manager(first, last, company) {
  Employee.call(this, first, last, company);
  this.workers = [];
};
 
Manager.prototype.hire = function(worker) {
  this.workers.push(worker);
  worker.manager = this;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
 * @class Worker
 * @inherits Employee
 */
function Worker(first, last, company) {
  Employee.call(this, first, last, company);
  this.manager = null;
};
 
Worker.prototype.getManagerName = function() {
  if (this.manager)
    return this.manager.getFullName();
  else
    return '';
};

Note the execution of Employee.call() in each of these constructors. When I run my test all the properties pass, but each test fails on the getFullName method and the getWebSite method, which is exactly what I expected since inheritance is not working for these yet. So, this means that the Manager constructor calls the Employee constructor, which in turn calls the Person constructor. And it all works well.

If you recall, methods are added to constructor functions through the prototype object of the function. In my next post, I’m going to see how I can inherit the prototype object, and the methods assigned to it, from one constructor to another.