This post describes a simple, object-based inheritance system for ECMAScript 5 compliant runtimes. First, I will explain how inheritance works in JavaScript/ECMAScript, and how inheritance chains are built usually to give you a deeper understanding what I’m talking about. After that, I’ll introduce the new Object.create() method found in ECMAScript 5 and an inheritance system built on top of that functionality.

The Prototype Chain / Inheritance in JavaScript

Inheritance in JavaScript bases on the Prototype Chain. Every object has an internal “[[Prototype]]” property pointing to another object or null. The notation with square brackets is used in the ECMAScript specification, I will stick to it in this post. This property typically isn’t accessible in a program – it is private to the JavaScript runtime, even though Gecko and some other runtimes have started exposing it through the non-standard __proto__ property.

When trying to access a property on an object (e.g. fooObject.barProp), the runtime will follow the prototype chain from object to object (using the [[Prototype]] property) until the property is found. If it is not defined throughout the whole prototype chain, undefined is returned.

The difference to class-based programming languages is, that property/method access is not resolved within a class hierarchy, but rather within an hierarchy of objects (“class instances”). Properties shared between instances (“shared properties” in ECMAScript terminology) and methods are defined on an object deeper in the chain, while “instance variables”(“own properties” in ECMAScript) are defined on the top-most object itself – the object you are working with.

Creating Inheriting Objects

The traditional method of creating objects with a specific prototype chain is to define a constructor function and set its prototype property to the desired object.

function MyConstructor() {
}

MyConstructor.prototype = {
    foo: "some property",
    bar: function() {
       return "some method";
    }
};

Now it is possible to create instances using the new operator:

var myInstance = new MyConstructor();

This code creates a new object, sets its internal [[Prototype]] property to MyConstructor.prototype, and executes MyConstructor in the context of the newly created object (“in the context” means that the this keyword points to the new object).

The instanceof operator allows us to check whether an object has been created by a specific constructor function:

myInstance instanceof MyConstructor; // true

(instanceof checks the whole prototype chain, and works with “subclasses”, too).
This coupling of constructor and instances is rather loose; when creating a second constructor with its prototype pointing to the same object as the first constructor, an object will be instance of both constructors:

function MySecondConstructor();
MySecondConstructor.prototype = MyConstructor.prototype;

new MyConstructor() instanceof MyConstructor; // true – ok
new MySecondConstructor() instanceof MySecondConstructor; // true – we’ve expected that
new MySecondConstructor() instanceof MyConstructor; // also true
new MyConstructor() instanceof MySecondConstructor; // true again

also, when changing the prototype property of a constructor to another object,

instanceof

yields a different result:

var myInstance = new MyConstructor();
myInstance instanceof MyConstructor; // true

MyConstructor.prototype = {};
myInstance instanceof MyConstructor; // false

The explanation is simple – since MyConstructor.prototype is not in the prototype chain of myInstance, the operator yields false

Object-Based inheritance

The latest edition of ECMAScript – ECMAScript 5th Edition – gives us a powerful new tool to create objects with a specific prototype: Object.create(). The method takes two parameters: The object to use as [[Prototype]] and an object containing property descriptors for the new object to create. Using Object.create allows us to short-circuit object creation and avoid the usage of constructor functions.

Creating two objects sharing a common prototype is easy:

var baseObject = {
    foo: "some property",
    bar: function() {
       return "some method";
    }
}

var myObject = Object.create(baseObject);
var mySecondObject = Object.create(baseObject);

Building an inheritance chain is also possible, but might get a little bit tedious when using property descriptors to add new properties:

var Person = {
    firstName: null, // the person’s first name
    lastName: null // the person’s last name
};

// “subclass” Person
var Employee = Object.create(Person, {
    id: { // the employees’s id
        value: null,
        enumerable: true,
        configurable: true,
        writable: true
    }
});

// create an instance of Employee
var david = Object.create(Employee);
david.firstName = "David";
david.lastName = "Aurelio";
david.id = 1;

// “subclass” Employee
var Manager = Object.create(Employee, {
    department: { // the manager’s department
        value: null,
        enumerable: true,
        configurable: true,
        writable: true
    }
});

// create an instance of Manager
var tobias = Object.create(Manager);
tobias.firstName = "Tobias";
tobias.lastName = "von Klipstein";
tobias.id = 0;
tobias.department = "Infrastructure";

You may have noticed two things in this example:

  1. Creating “instances” of an object or “subclassing” it are basically the same process: We create a new object with its prototype set to the base object. Personally I like this fact; it just feels natural in an prototype-based inheritance model like the one in ECMAScript/JavaScript.
  2. Not using a constructor function shows how useful such an initialization function would be for instance creation.

Inheritance System Based On Object.create()

For me, using Object.create() felt incredibly natural compared to fiddling around with constructor functions and the prototype object attached to them – that doesn’t mean having a constructor is bad; they are really useful for setting up an instance. But the coupling of constructor, constructor.prototype, and instances is too loose in my eyes, and makes it unnecessarily hard to understand inheritance in JavaScript.

What I don’t like is the verbosity of the descriptor syntax. Granted, it gives us a lot of possibilities, but for most cases it is too cumbersome. In addition, I wanted to have a constructor when creating objects that are supposed to be used like a class instance in a class-oriented programming language.

I came up with a light-weight inheritance system that provides separate methods for the two cases where I use Object.create(), object extension and instantiation.

Have a look at this gist.
[gist id=838778 file=base.js]

You can use BaseObject as base for your inheritance/object tree, or simply copy all of its methods onto your own base object. Both instantiation and extension are done through Object.create, but work pretty differently. Let me go into detail and explain the four methods present on BaseObject.

  • The _construct method acts like a regular constructor: It is executed in the context of a newly created instance right after creation in the context of that instance (this works as you would expect). You may overwrite this constructor as often as necessary. You can access parent constructors through the _super convenience method (see below for details). The dangling dash suggests you should regard this method as private. Usually, you never need to call it manually.
  • create is used for instantiation. Whenever you need a new “instance” of a base object (that you might use like a class), you’d simply call “create()” on it: var myInstance = MyBase.create(arg1, arg2);. The instance is created, and its “_construct” method is called with all arguments passed to “create()”. Eventually, the instance is returned. Normally, you might not need to overwrite the base create method, but you may do so for complete control over the instance creation process if needed.
  • Use the extend method to create “subclasses” – it differs from creating instances in two aspects: the constructor isn’t called, and you can easily create additional properties on the new object: The first parameter is a simple object. All of its properties are copied onto the new object (you may also pass null). The second (optional) parameter is an object containing property descriptors for properties to be created on the new object. Check the example below for details. This system allows it to use simple properties without taking the power of property descriptors away from you.
  • _super is a convenience method to call overwritten parent methods in the context of the current object. It works similar to python’s super and takes three arguments: A reference to the object that overwrites a method (not the name of the parent object), the name of the overwritten method (as string), and an array of parameters/an arguments object. Just take a look at the example to get the idea how it works.

Generally, you should not overwrite the “extend()” and “_super()” methods. “create()” may be overwritten for more control over the creation process of instances. “_construct()” can be defined as often as needed.

This example is a rewrite of the Person/Employee/Manager example using BaseObject as parent of Person:

var Person = BaseObject.extend({ // using only properties that are copied over
    firstName: null,
    lastName: null,
    _construct: function(firstName, lastName){
        this.firstName = firstName;
        this.lastName = lastName;
    },
    toString: function(){
        return this.firstName + " " + this.lastName;
    }
});

var Employee = Person.extend(null, { // using only property descriptors
    id: { // the employees’s id
        value: null,
        enumerable: true,
        configurable: false, // can’t be deleted
        writable: true
    },
    _construct: { // has only a value, is not enumerable, writable, or deletable
        value: function(firstName, lastName, id){
            this._super(Employee, arguments); // calls the parent constructor with all received arguments
            this.id = id;
        }
    },

    toString: {
        value: function(){
            return "[id: " + this.id + " / " +
                this._super(Employee, "toString") + "]"; // calls parent "toString"
        }
    }
});

// create an instance of Employee
var david = Employee.create("David", "Aurelio", 1);
david.toString(); // "[id: 1 / David Aurelio]"

var Manager = Employee.extend({
    // simple properties
        _construct: function(firstName, lastName, department, id){
            this._super(Manager, [firstName, lastName, id]); // calls the parent constructor with three parameters
            this.department = department;
        },
        toString: function(){
            return this._super(Manager, "toString") + // calls parent "toString" method without arguments
                " (Manager)";
        }
    },
    { // property descriptors
        department: {
            value: null,
            enumerable: true,
            configurable: false,
            writable: true
        }
});

// create an instance of Manager
var tobias = Manager.create("Tobias",  "von Klipstein", "Infrastructure", 0);
tobias.toString(); // "[id: 0 / Tobias von Klipstein] (Manager)"

What’s Nice

What I really, really like is the fact everything feels just natural – we’re dealing with a set of objects, not with a constructor/prototype construction (which might be one of the reasons inheritance in JS is so difficult to understand when coming from a class-based language). For example: Where would you define a “static” member in the classic approach? On the prototype? It’s in the inheritance chain and can be overwritten by “subclasses”, but might be hard to reach from other places (think of Shape.prototype.SQUARE. Not cool. On the constructor? That’s nicer for access (Shape.SQUARE), but places the property outside of the inheritance chain, thus making it impossible to overwrite it. In addition, I feel that using this.SQUARE from methods feels more natural.

Using a hierarchy of objects solves all that little inconsistencies. Yum!

Extending incredibly flexible: since extend() is present on every object created by extend()/create(), you can extend everything with new properties.

What’s Not So Nice

Since the objects we use in a “class” role are objects, not functions, we can’t use the new operator. After years of using it, I really have to adopt to the use of create() instead.

Also, instanceof only works with constructor functions. Use the .isPrototypeOf() method of ECMAScript 5 instead.

Compatibility is still an issue. The browser/runtime must support the new ECMAScript 5 features Object.create(), Object.getOwnPropertyNames(), Object.getOwnPropertyDescriptor(), and Object.getPrototypeOf(). Browsers supporting the required functionality include: Firefox 4, Safari 5, Chrome 9,
Internet Explorer 9 (untested). Opera has no support for these features yet.