A Programmer’s Tour Of Javascript

Prototypes and Inheritance

People who are coming from other languages will notice a glaring absence in JavaScript’s object syntax. There is no way to specify inheritance. You can’t declare an object’s supertype; you can’t implement an interface; you can’t include mixins or use traits. Those kinds of inheritance need classes of some sort, and JavaScript doesn’t have them.

Instead, JavaScript uses prototype objects. A prototype is an object whose properties are inherited by another object. Every JavaScript object – including prototype objects – has a hidden reference to its prototype. The only exception is the prototype of the Object object; it is the “base prototype” of all JavaScript objects, and its prototype is null. This results in a linked list of prototype objects, starting with the current object’s prototype, and ending with theObject prototype. This list of prototype objects is called the prototype chain.

Here’s how inheritance is handled. During execution, the JavaScript interpreter encounters a qualified identifier. A qualified identifier includes the name of an object (which I’ll call the “original” object), followed by a string (ostensibly representing a member). In JavaScript, both myObject.myField and myObject["myField"] are qualified names.

The interpreter first tries to match the string to a member of the current object; these member are called own properties. (Remember that in JavaScript, objects are hashes, so this is a simple matter of looking up a key in the hash.) If no match is found, then it follows the reference to the prototype object, and tries to match the string to any of its members. It looks through the entire prototype chain in this manner. If it gets all the way up to the Object prototype without finding a match, it returns undefined.

But the first time it finds a match in the prototype chain, it binds the member to the original object. So if the member is a method, this references the original object, and not the prototype object where that method was defined. This means that methods originating up the prototype chain can be used as if they were the original object’s own methods. It also means that any object’s own properties can shadow the members of prototype objects further up the chain. This has the same effect as method overriding in classical object-oriented languages.

The reference to a specific object’s prototype is supposed to be unavailable to programmers. However, quite a few browsers support the so-called hidden __proto__ property. As the name suggests, it is a property of all objects, and references that object’s prototype object. It is not supported in all browsers, so its use is discouraged in production code. But it is useful as a learning tool, so I’ll use it occasionally in this article.

Every object also has a reference to its constructor function. This is the function that was used to construct the object. Unlike the prototype reference, it is not a hidden reference, but a public member; naturally enough, that member is named constructor. Because prototypes are objects, they have their own constructor members as well.

This is mainly useful when using the instanceof operator. I mentioned it before, but now it’s time to learn how it works. You’ve probably seen this operator in other languages, and it serves the same purpose in JavaScript. The instanceof operator is a binary operator; the left-hand operand must be an object, and the right-hand operator must be a “type.” Its purpose is to determine the polymorphic behavior of the object: to see if the object behaves as a member of that type.

In classical object-oriented languages, a “type” is a class (or an interface in Java). But JavaScript doesn’t use those, so its mechanism is different. The right-hand side of JavaScript’s instanceof operator must be a function. This is because the operator is not checking a class heirarchy, but a constructor heirarchy. The operator examines the prototype chain of the object on the left-hand side, and if the function appears as a constructor anywhere in that chain, then it returns true.

This works because in JavaScript, the prototype chain begins at the object’s constructor function.

Remember that functions are also objects. But there’s one big difference between function objects and other kinds of objects: function objects have a prototype property. By default, this property holds a reference to an empty object whose constructor property is set to that function object.

Let’s examine what happens when a function is used to construct an object. For the sake of discussion, let’s call our constructor function ConstructMe.

  1. A temporary, empty object is created.
  2. The temporary object’s hidden prototype reference (i.e. __proto__) is set to ConstructMe.prototype.
  3. The temporary object’s constructor reference is set to ConstructMe.prototype.constructor.
  4. The ConstructMe function is executed. Any references to this will be bound to the temporary object.
  5. If ConstructMe returns an object, that object will be used, and the temporary object will be garbage collected. Otherwise, the temporary object becomes a permanent instance.

In this way, the ConstructMe function becomes part of the object’s chain of constructors, and using instanceof ConstructMe on that object will return true.

But this seems like an awful lot of trouble to go through just for one lousy operator. And it is. The true power of prototypes doesn’t become apparent until you treat the prototype property like any other member.

Like all JavaScript properties, the prototype property is dynamic. You can add or delete the properties of the prototype object, like you would for any other object. And you can set the prototype property so that it references an entirely different object. All of this can happen at runtime.

This creates nearly endless possibilities for object enhancement. In general, these enhancements do one of two things: they create properties that are shared among instances, and they specify inheritance.

Specifying Shared Properties

First, we need some terminology. Simply talking about an “object” is going to lead to a lot of confusion, because it won’t be clear if we’re talking about the prototype object of the constructor, or the object that is created using that constructor. We should have separate terms for each.

The prototype property will be the property of the constructor function. The prototype object will be the object that the property references. When I just say “prototype,” I mean the object; I will explicitly use the word “property” when talking about the reference itself.

An instance object, or simply “instance,” will be an object that is created using the constructor function. An instance is created using the new keyword and bound to an identifier (usually a variable, but it may also be a parameter).

Within a single scope, there can only be one function with a particular name. But there can be a slew of different instances that are constructed using that function. All of those instances will share the same prototype. This means that you can add members to the prototype, and those members will be shared among all of the instances.

This is the proper way to add methods to a type. Previously, we created a method by adding a member to this inside the constructor, like so:

function MyConstructor() {
    this.myMethod = function() {
        // code here
    }
}

But remember that this technique creates a separate function object for every single instance created with MyConstructor. We know that inside any function, this binds to the invoking object – in our case, the individual instance. So we would save a lot of memory if the methods could be shared among all instances.

We do this by adding the member to the prototype instead. Let’s rewrite the above code so it does that:

function MyConstructor() {};
MyConstructor.prototype.myMethod = function() {
    // code here
}

Remember that this happens at runtime, so any methods you add to the constructor’s prototype will immediately become available to all instances:

function MyConstructor() {};
var one = new MyConstructor();
var two = new MyConstructor();
// Add the method after construction
MyConstructor.prototype.myMethod = function() {
    return "Present";
}

> one.myMethod()
  "Present"
> two.myMethod()
  "Present"

You can also add properties to the prototype. But remember that properties cannot be read-only, so if you set the property of one instance, you set it in all instances. (It has the same effect as using static variables in other languages.) This is usually not what you want.

So, you should generally follow this rule: set properties in the constructor, but add methods to the prototype.

In JavaScript, a property that is unique to a specific instance is called an own property. (It is analogous to an instance variable in other languages). To tell whether a property is an own property, or inherited from the prototype chain, all JavaScript objects have the hasOwnProperty() method. Perhaps ironically, it is not itself an own property, but is inherited from Object.

Specifying Inheritance

During construction, an instance’s prototype is set to the prototype of the constructor function. Each instance inherits the properties and methods of this prototype. So, to specify inheritance, you set the prototype property of the function itself.

The simplest way to do this is to construct a new “superobject,” and set the constructor’s prototype to that object. Let’s see a really basic example:

function Supertype() {};
function Subtype() {};
Subtype.prototype = new Supertype();
var sub = new Subtype();

> sub instanceof Supertype;
  true

There’s one other thing you should do when you replace the prototype like this. Remember that the constructor property is also a property of the prototype. If you replace the prototype, it’s also a good idea to reset the prototype’s constructor property to the “subtype” constructor:

Subtype.prototype = new Supertype();
Subtype.prototype.constructor = Subtype;

The main disadvantage of this inheritance technique is that the own properties of the “superobject” are shared among all instances. Setting the property in one instance will set that property in all instances. This is generally not what you want. In fact, you probably don’t want to use the own properties of the “superobject” at all; you want to use the properties of its prototype.

“Well, that’s easy,” you may say, “just set the subtype’s prototype to the supertype’s prototype.” But that comes with its own major pitfalll. If you follow the general rule above, adding a property to the “subtype” will also add it to the “supertype.” Consider this code:

function Person() {};
function Sloan() {};
Sloan.prototype = Person.prototype;
// Add a method to the "subtype"
Sloan.prototype.phone = function() {
    return "We've had a... death in the family.";
};
// Construct a "supertype" object and see what happens
var edRooney= new Person();

> edRooney.phone();
 "We've had a... death in the family."

Obviously, Ed Rooney would never believe such a thing.

The most common solution is to create an intermediate object to specifically act as a prototype. The intermediate object uses a temporary constructor function (usually called F by convention), and that function’s prototype is set to the “supertype” prototype:

function Supertype() {};
function Subtype() {};
// Temporary constructor
function F() {};
F.prototype = Supertype.prototype;
F.prototype.constructor = Subtype;
Subtype.prototype = new F();
var sub = new Subtype();

> sub instanceof Supertype;
  true

JavaScript guru Douglas Crockford proposed that all programmers write an object() function, which creates a new object from an existing one. Here’s the code:

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

Crockford wasn’t the only one who thought this was a good idea. ECMAScript 5 introduced the Object.create() method, which does something very similar. As its first parameter, it takes an object to use as the prototype.

An optional second argument is a properties object. This is an object whose member names are the names of additional properties to create in the instance. The values of the properties object are property descriptors: objects whose key/value pairs define how that property should behave. Here’s an example:

function Person() {};
var sloanProperties = {
    phone: {
        enumerable: true,
        configurable: true,
        value: function() {
            return "We've had a... death in the family.";
        }
    }
};
var sloan = Object.create(Person.prototype, sloanProperties);

> sloan.phone();
  "We've had a... death in the family."

Property descriptors are incredibly powerful. They can define properties that are read-only, are not enumerable in for/in loops, or cannot be changed. The property descriptor syntax is the same one used by Object.defineProperty(), so if you want the details, you can read about that method on the Mozilla Developer Network or the Internet Explorer Dev Center.

Unfortunately, the Object.create() method is not supported by browsers that came out before 2009 – particularly IE 8. If you want your code to be compatible with all your users, you won’t be able to use that method for a couple of years.

Calling the “super constructor”

Most classical object-oriented languages have a reference to the superclass’s methods and fields. Java uses the super keyword for this purpose.

For some reason, “super” is a reserved word in JavaScript. But don’t let this fool you – JavaScript doesn’t use it. (I’m guessing the original creators of JavaScript simply copied Java’s list of reserved words.)

Instead, you have to call the “super constructor” function explicitly, by name. (People who know C++ will be familiar with this idiom.) But if you just call it as a function, this inside the “super constructor” will be bound to the global object, not the instance.

The solution is to explicitly bind this inside the “supertype” constructor to this inside the “subtype” constructor. You can do that using any of the explicit binding methods: call(), apply(), or bind(). (But remember that bind() isn’t supported in older browsers.)

One common technique is to accept the “supertype” constructor’s arguments, in the same order, in the “subtype” constructor’s parameter list. Any “subtype” parameters are simply appended to the parameter list. This means that you can simply pass the arguments object to the apply() method; the additional arguments are ignored.

Here’s an example:

function Person(name) {
    this.name = name;
}
function Student(name, id) {
    Person.apply(this, arguments);
    this.id = id;
}
var me = new Student("Karl", 1234);

> me.name;
  "Karl"
> me.id;
  1234

There is a “gotcha” with this method, which I’ll discuss next.

Issues with Returning Objects

When we talked about functions as constructors, we returned objects from those functions to avoid polluting the global namespace. This happened in two situations: when we did an explicit type check, and when we returned a “revealing object.” Both cause issues with the prototype chain.

In the “type check” situation, we make sure that this is actually an instance of the constructor type – that is, that the constructor function is in the prototype chain. That works perfectly when our constructor is not a “supertype” constructor. But if it is, then we can’t call the “subtype” constructor unless we have already set the prototype to the “supertype.” If we do, this will fail the type check – and the returned object will not be this from the “subtype” constructor.

Here’s some code that illustrates the problem:

function Person(name) {
    // explicit type check
    if ( !(this instanceof Person) ) {
        return new Person(name);
    }
    this.name = name;
}
function Student(name, id) {
    Person.apply(this, arguments);
    this.id = id;
}
// Person isn't in the prototype chain of Student,
// so the type check will fail!
var me = new Student("Karl", 1234);

> me instanceof Person;
  false
> me.name;
  undefined

The only solution is to explicitly set the prototype before you create any instances. You should do this anyway, since it’s the accepted way to declare inheritance. Until you do, your objects are “really” inheriting from Object.

The other time we return an object is when we return a “revealing object,” as we do in the revealing module pattern. This is when we use object literal notation to return a new object whose members are privileged methods. When you do this, the object that is returned is a plain ol’ JavaScript object. That means it has absolutely no inheritance relation to the function that constructed it. Its prototype will be Object.prototype, not the prototype of the constructor function.

This is significant for two reasons:

  1. The instanceof operator will not behave as expected; it will return false for any function except Object.
  2. The type is not extensible through its prototype. Adding properties to the constructor’s prototype will have no effect at all on its instances.

Here is some simple code that demonstrates the issue:

// Person with private "name" variable
function Person(name) {
   function getName() {
       return name;
   }
   return {
       getName: getName
   }
}
// This does nothing for any Person instance!
Person.prototype.extend = function() {
    return "Extended: " + this.getName();
}
// Try it out...
var p = new Person("me");

> p instanceof Person;
  false
> p.extend;
  undefined

One solution is to create a temporary constructor inside the “type” constructor, and return a new instance of that object:

// Person with private "name" variable
function Person(name) {
   function getName() {
       return name;
   }
   function F() {
       this.getName = getName;
   }
   F.prototype = Subtype.prototype;
   return new F();
}
// NOW, this will work
Person.prototype.extend = function() {
    return "Extended: " + this.getName();
}
// Try it out...
var p = new Person("me");

> p instanceof Person;
  true
> p.extend();
  "Extended: me"

Note that you can’t reference the “private variables” when you’re extending the prototype. If you tried to access the name variable inside the extend method that you added later, you’d get a ReferenceError. The properties assigned to this in the temporary constructor are the only ones that are revealed to the client code. (This is exactly as it should be; otherwise, they shouldn’t be private variables in the first place.)

Implementing Multiple Inheritance

All classical object-oriented languages have some kind of technique for implementing multiple inheritance. For example, C++ allows classes to inherit from multiple classes, and Java has interface types.

A JavaScript object can only have one prototype, so there is no native way to implement “multiple inheritance” with multiple prototype objects. But you can easily create it. Since prototype objects are dynamic, adding functionality is simply a matter of defining it in the prototype. If you want to “inherit” from multiple prototypes, you can simply copy the properties of those prototype objects. The result is something like a Mixin in other languages.

Here’s a function called Mixin() that will do it. It takes any number of prototype objects, and creates an object with the properties of all of them:

function Mixin() {
    var mixed = {},
        i = 0,
        proto,
        prop;
    for (i = 0; i < arguments.length; i++) {
        proto = arguments[i];
        for (prop in proto) {
            if (proto.hasOwnProperty(prop)) {
                mixed[prop] = proto[prop];
            }
        }
    }
    return mixed;
}

You can use this function to construct an object that is a mixture of different prototypes:

function Bird() {}
Bird.prototype.fly = function() {
    return "Flying away";
}
function Horse() {}
Horse.prototype.gallop = function() {
    return "Galloping along the ground";
}
var pegasus = new Mixin(Bird.prototype, Horse.prototype);

> pegasus.fly();
  "Flying away"
> pegasus.gallop();
  "Galloping along the ground"

As you can see, prototype-based inheritance is incredibly powerful. You can get along fine with what I talked about, but I’ve really just scratched the surface. To go deeper, read the JavaScript books that I recommend at the end of the article.

Advertisements

About Karl

I live in the Boston area, and am currently studying for a BS in Computer Science at UMass Boston. I graduated with honors with an AS in Computer Science (Transfer Option) from BHCC, acquiring a certificate in OOP along the way. I also perform experimental electronic music as Karlheinz.
This entry was posted in JavaScript, Programming and tagged . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s