Prototypal Inheritance

itayco edited this page Jun 2, 2014 · 3 revisions

Although augment is primarily used as a classical inheritance function it can also be used for prototypal inheritance. In prototypal inheritance you don't have any constructor functions. Objects simply inherit from other objects. In this respect it's very similar to Crockford's Object.create function. To make prototypal inheritance easier let's create a base prototype called instance which every prototype and object derives:

var instance = {
    is: function (object) {
        return object.isPrototypeOf(this);
    },
    create: function () {
        return augment(this, constructor, arguments);
    }
};

function constructor(args, base) {
    base.init.apply(this, args);
}

The instance prototype has two helper functions, is and create, which correspond to the instanceof and new operators respectively. So let's begin with prototypal inheritance. We'll create the same algebraic data types as we did in Getting Started:

var extend = augment.extend;

var shape = augment(instance, function () {
    this.circle = extend(this, {
        init: function (x, y, r) {
            this.x = x;
            this.y = y;
            this.r = r;
        },
        area: function () {
            return Math.PI * this.r * this.r;
        }
    });

    this.rectangle = extend(this, {
        init: function (x1, y1, x2, y2) {
            this.x1 = x1;
            this.y1 = y1;
            this.x2 = x2;
            this.y2 = y2;
        },
        area: function () {
            return Math.abs((this.x2 - this.x1) * (this.y2 - this.y1));
        }
    });
});

As you can see the code remains largely the same. The only differences are:

  1. The names Shape, Circle and Rectangle become shape, circle and rectangle respectively.
  2. The prototype shape now derives from instance instead of Object.
  3. The constructor function is now called the init function.

So let's see how to use it:

var circle = shape.circle.create(0, 0, 10);
var rectangle = shape.rectangle.create(0, 0, 8, 6);

alert(circle.is(shape));              // true
alert(circle.is(shape.circle));       // true
alert(circle.area());                 // 314.1592653589793

alert(rectangle.is(shape));           // true
alert(rectangle.is(shape.rectangle)); // true
alert(rectangle.area());              // 48.0

That's all that there is to prototypal inheritance. So what are the advantages of prototypal inheritance over classical inheritance?

  1. Since create is a function you can use it in conjunction with apply. You can't use new in conjunction with apply.
  2. Forgetting to use new leads to unexpected bugs and global variables. It's not possible to forget create.
  3. Prototypal inheritance is cleaner. It teaches you that there's nothing special about .constructor or .prototype. In fact you don't really need constructor functions for inheritance in JavaScript. All you do need are objects.

If you want to know more about prototypal inheritance then read my blog post on Why Prototypal Inheritance Matters.