Sam Elsamman

Nerdy Musings and Other Stuff

Why I don’t Use Prototypal Inheritance in JavaScript

Object oriented semantics in JavaScript do not really exist, leaving a vacuum that has been filled with dozens of popular techniques for achieving this.  Early on Douglas Crockford, a senior architect at Yahoo shared with the community some of the mechanics of implementing classical inheritance.  Since then many popular frameworks implemented versions of classical inheritance.  Later in 2006 he retracted his support for classical inheritance in favor of so called prototypal inheritance.  In this post I will review the differences between the two and explain shortcomings of the latter model that make me very wary of using it.

Classical Inheritance Review

While it is far from pretty, Javascript is well able to do classical inheritance.  I covered this in my last two posts, JavaScript Inheritance Au Naturel and JavaScript Inheritance Minimalist Helper.  The main features of classical inheritance in JavaScript are:

  • An object is created from a function which serves the role of a class
  • A class may inherit behavior from a parent class, thus forming a hierarchy
  • The class function serves as a constructor to initialize data properties
  • The class prototype property anchors methods to be inherited by objects

As we know, JavaScript, knows nothing about classes.  So all of this is implemented using the only OO mechanism JavaScript has at it’s disposal – the prototype chain.  Any object can inherit properties from objects in it’s prototype chain.  When referencing a property that is not found in the object, the JavaScript run-time walks up the prototype chain and returns the first property it finds by that name.  The prototype chain is established by applying the new operator to a function call:

function Animal (gender, sound) {
	this.gender = gender;
	this.sound = sound;
	this.traits = {};
}
Animal.prototype.hello = function() {alert(this.sound)}
var lion = new Animal("male", "roar");
lion.hello() // pops up roar

Here the new operator conveniently sets up the first link in the prototype chain for lion to be Animal’s prototype.  When you call hello() from lion, the prototype chain will be examined and  the hello function that is referenced in Animal’s prototype will be called.

This is great for functions because you only need one instance of the function and so the one instance can be in the prototype property that is inherited.    Data properties on the other hand are always created in the constructor in classical inheritance.  We will see why this is important as we examine prototypal inheritance.

Prototypal Inheritance

As we saw in classical inheritance the prototype chain is used to find functions in an object. With prototypal inheritance the prototype chain is often used to find data properties as well.

Prototypal inheritance dispenses with the idea of classes since they really are not part of the JavaScript language.  Instead you create objects from other objects.  These objects may serve the role of a class if you choose to model things that way or if you are feeling less formal they can just be templates for your new objects.  Proponents of prototypal inheritance argue that this is more flexible and natural than classical inheritance.  It is also syntactically cleaner since you can use JavaScript Object Notation JSON to create properties.

With prototypal inheritance you don’t have to use constructors.  You can create an initialize function if you see fit to do so but you must depend on the consumer of your object template to call this initialize function.

EMCA edition 5 has added an Object.create function to create an object from another object.

var Animal = {
	gender: "",
	sound: "",
	traits: {},
	hello: function() {alert(this.sound)}
}
var lion = Object.create(Animal);
lion.gender = "male";
lion.sound = "roar"
lion.hello() // pops up roar

Note that older browsers (e.g. IE versions prior to 9) don’t support Object.create but you can simulate it as follows:

function ObjectCreate(o) {
     function F() {}  // create a dummy function
     F.prototype = o; // Set it's prototype to inherit o
     return new F();  // new establishes the prototype chain.
}
var lion = ObjectCreate(Animal);

Which ever way you create the object, the prototype chain will be established such that when you reference lion.gender you may be referencing the instance in the animal object. I say may be because if you set lion.gender to a new value you are actually adding a gender property to the lion object. This is in fact what you want since you want the modified version of gender only in the lion object and not in all objects created from Animal.

But what about the traits property? Since traits is a reference to an object you are not going to reassign it and instead you are going to access it’s properties as in lion.traits.legs = 4. Here we see the main gotcha of prototypal inheritance:

var lion = Object.create(Animal);
lion.traits.legs = 4;
var bird = Object.create(Animal);
bird.traits.legs = 2;
alert(lion.traits.legs) // shows 2!!!

So there are three ways to fix this:

  1. Put an initialize function in the Animal object that sets this.traits to an empty object and be sure to call it after creating objects from Animal
  2. Initialize traits to an empty object after creating any objects form Animal
  3. Define a getter for traits that initializes it if it is not already intialized.  Unfortunately standard getters and setters are not supported in Microsoft browsers prior to IE9

If you decide to use an initialize function this creates a dilemma.  Do you always have an initialize function in order to be consistent?  If you don’t provide one and then later add something like a traits property you would have to find all the places in your code where you create the object and make sure to call it’s initialize function.  Secondly you now find you have two ways to initialize data properties – in the JSON or in the initialize function and you better make sure you get this right.  So you might just decide to always have an initialize function and maybe even go as far as initializing all data properties there.  As you start to slide down the slippery slope things start to look a lot like classical inheritance.  So why not just enjoy the consistency of classical inheritence to start with?  For me the answer is clear.

And did I mention the instanceof operator?

lion instanceof Animal // returns true with classical inheritance

It doesn’t work with prototypal inheritance.  While this is not a show-stopper it has made my life easier on numerous occasions and I see no reason to toss it out with the bath water.

The reality is that most complex software will be developed using a framework and most frameworks support their own way of doing object oriented programming.  Most of them veer towards classical inheritance.  Not surprisingly YUI, Yahoo’s framework uses both patterns.  With the advent of object.create in EMCA edition 5 there will be many who now want to move a way from classical inheritance.  I suggest that we think twice before taking that leap.

4 Responses to “Why I don’t Use Prototypal Inheritance in JavaScript”

  • Noel Abrahams says:

    Thanks, Sam. I agree with the sentiment.

    Another point in favour of classical inheritance is that it permits checking of data arguments passed via the constructor. With prototypal inheritance you need to assume that the data provided in the JSON is correct, or alternatively the actual checking has to take place completely outside of the object.

    e.g.
    var creds = ajax.getCredentialsForId(5); // returns { gender: “malformed”, sound: “roar” }

    lion.gender = creds.gender;
    lion.sound = creds.sound;

    console.log(“This is a ” + lion.gender + ” lion”); // oops!

    With the classical method one can have:

    function Animal(gender, sound) {

    if(gender !== “male” && gender !== “female”){
    throw “Invalid gender”;
    }

    this.gender = gender;
    this.sound = sound;
    }

    Well at least you know you are not going to embarrass yourself.

    While you could certainly check the ajax response for the prototypal inheritance case, the point is that logic has to occur outside of the object, which breaks encapsulation IMHO.

    It is difficult to have an armchair debate about classical vs. prototypal because to actually see the benefits of the classical method one has to have a complex JavaScript application laid out for analysis.

    Then I believe the benefits will become clear.

  • kleppstuhl says:

    Hi Sam,

    I think the main difference of the two aproaches is the use of the contructor. I wouldn’t call this “Classical Inheritance”, because as with Object.create you could also assign properties to the constructor function:

    Animal.prototype.gender=”";

    which are then available in the with new created objects (If you don’t overwrite them). So it’s prototype inheritance with a constructor.

  • d13 says:

    var animal = {
    traits: {}
    };

    var lion = Object.create(animal);
    lion.traits = {legs: 4};
    var bird = Object.create(animal);
    bird.traits = {legs: 2};

    //Now the lion has 4 legs
    console.log(lion.traits.legs);

  • Sam Elsamman says:

    Very true d13. lion.traits = {legs: 4} works well after creating the animal but this style would not be suitable in code down the line. Say for example you passed lion to a function that was responsible for setting a diet for animals and had no concern for legs. You would not want to then say lion.traits = {eats: “meat”} or it would wipe out the legs property.

    What you are pointing out, it seems, is that the creator of the object needs to initialize embedded objects and once this is done anyone can just set properties (lion.traits.eats = “meat”). My preference is for a mechanism where this can be accommodated by the author of the object rather than the consumer.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>