Sunday, November 9, 2014

JavaScript's Prototypal Inheritance

The Prototype Chain

If you've ever implemented inheritance in JavaScript, but are not yet aware of prototype, you're a testament to the flexibility of the language. It's as elegant or robust as you want it to be. Coming from AS3, myself, I found myself wanting to build a structure closer to a class-based inheritance model, but prototype won out. Let's jump right into it.

Prototype is the link between all JavaScript objects that inherit from or are instantiated from the same base object. Here's an example:

function Foo(x, y) {
    this.x = x;
    this.y = y;
};

Foo.prototype.z = 10;

var f1 = new Foo(1,2);
console.log(f1.x, f1.y, f1.z); // prints 1 2 10

var f2 = new Foo(3,4);
console.log(f2.x, f2.y, f2.z); // prints 3 4 10

// Properties added to the prototype will be shared
// across objects, even after instantiation.

Foo.prototype.a = 100;

console.log(f1.a); // prints 100
console.log(f2.a); // prints 100

Prototypal Inheritance

The following block of code demonstrates Douglas Crockford's prototypal inheritance operator, in which the OldObject is added to the NewObject's prototype, inheriting all of it's public properties and methods. Keep in mind that with this pure prototypal pattern you are to use plain objects, not functions. No initialization needed.

if (typeof Object.create !== 'function') {
    Object.create = function (o) {
        function F() {}
        F.prototype = o;
        return new F();
    };
}

Vehicle = {
    wheels: 4,
    headlights: 2,
    fuelType: "diesel",
    go: function() { 
        console.log('Going'); 
    }
};

// Usage
var car = Object.create(Vehicle); // NewObject = Object.create(OldObject);
console.log(car.wheels, car.headlights, car.fuelType) // prints 4 2 "diesel";

Motorcycle = Object.create(Vehicle);
Motorcycle.wheels = 2;
Motorcycle.headlights = 1;

var bike = Object.create(Motorcycle);
console.log(bike.wheels, bike.headlights); // prints 2 1

Classical Inheritance

The following example tries to follow a more class-based style of inheritance by attaching Vehicle's prototype to Motorcycle and allowing you to overwrite the constructor. In case you're not sure what a constructor is in terms of JavaScript functions, it's essentially everything that executes within the scope the main function. So in the following example, everything within the scope of Vehicle, but not within the scope of this.go() fires.

Vehicle = function() {
    this.wheels = 4;
    this.headlights = 2;
    this.fuelType = "unleaded";
    this.go = function() {
        console.log('Going'); 
    };
};

var car = new Vehicle();
console.log(car.wheels, car.headlights, car.fuelType); prints 4 2 "unleaded"

Now that we've declared Vehicle, Motorcycle may inherit from it. Motorcycle's constructor contains a call to Vehicle's constructor through apply, and then adds any overrides or custom properties and functions thereafter. In this case, a motorcycle only has two wheels and one headlight.

Motorcycle = function() {
    Vehicle.apply(this, arguments);
    this.wheels = 2;
    this.headlights = 1;
};

Motorcycle.prototype = Object.create(Vehicle.prototype, {
    constructor: {value:Motorcycle, configurable:true}
});

var bike = new Motorcycle();
console.log(bike.wheels, bike.headlights, bike.fuelType); prints 2 1 "unleaded"

So there it is. If you're interested in learning about another way to implement inheritance in JavaScript, make sure to also check out John Resig's approach. Also, check out this article to get a deeper understanding of call and apply.

No comments:

Post a Comment