Five ways to create objects – part 2: Inheritance

Let me start by saying I think inheritance is somewhat overrated in JavaScript. A lot of the inheritance you are going to need is already created for you: Function, String, Number etc. all inherit Object via its prototype.

I actually wonder if more energy goes into intellectual exercises around JavaScript inheritance than into using inheritance for real time solutions. Yes, JavaScript has an excellent inheritance mechanism but lets face it all those examples with furry animals, hierarchies of wheeled vehicles and the like have little real world application in client side coding.

How often do your new objects really need to inherit from other new objects you have created? By its nature the client object modelling is essentially flat (like your monitor). If you find yourself in JavaScript creating complex java-style object models with layers of inheritance then you might want to ask yourself why. Ajax allowed us to defer to the server where we used to have to clone our business/server logic on the client. I’d argue such complex data structures are best left to the server, because they perform better, are more easily distributed across subsystems and are probably more suited to classical OOP.

With that said, JavaScript does offer a very nifty inheritance strategy – there are no classes – objects inherit from objects. Period. It’s clean and its simple.

So here goes..

Last time I demonstrated five ways to create objects in JavaScript. Now, as promised here’s how to apply inheritance in each case. Clearly some cases are more useful than others.

Lets say our notepad from last week’s example might need to inherit some properties from a more generic “panel” component.

1. Simple Object Literal

var myApp = {};
myApp.panel = {};
myApp.panel.toggleDisplay = function() {
    this.displayed = (this.displayed==="none")? "" : "none";
}

myApp.panel.defaultWidth = 300;
myApp.notepad = {};
myApp.notepad.writeable = true;
myApp.notepad.font = 'helvetica';
myApp.notepad.setFont = function(theFont) {
    myApp.notepad.font = theFont;
}

//OK not inheritance at all. But best we can do, since notepad has no relation to panel.
myApp.panel.toggleDisplay.call(myApp.notepad);
myApp.notepad.defaultWidth = myApp.panel.defaultWidth;

2. Nested Object Literal

var myApp = {};
myApp.panel = {
    toggleDisplay : function() {
        this.displayed = (this.displayed==="none") ? "" : "none";
    },
    defaultWidth : 300
};

myApp.notepad = {
    writeable: true,
    font: 'helvetica',
    setFont: function(theFont) {
        this.font = theFont;
    }
};

//Same brute-force inheritance as example (1)
myApp.panel.toggleDisplay.call(myApp.notepad);
myApp.notepad.defaultWidth = myApp.panel.defaultWidth;

3. Constructor using Object Literal (courtesy of Douglas Crockford)

var myApp = {};

myApp.Panel = function(defaultWidth ) {
    var that = {};
    that.defaultWidth = defaultWidth ;
    that.toggleDisplay = function() {
        that.displayed = (that.displayed==="none") ? "" : "none";
    }
    return that;
}

myApp.Notepad = function(defaultFont, width) {
    var that = myApp.Panel(300);
    that.writeable = true;
    that.font = defaultFont;
    that.setFont = function(theFont) {
        that.font = theFont;
    }
    return that;
}

//true inheritance without using new or prototype (courtesy of Douglas Crockford)
myApp.notepad1 = myApp.Notepad('helvetica',300);
myApp.notepad1.defaultWidth;

4. Simple Constructor for new

var myApp = {};

myApp.Panel = function(defaultWidth) {
    this.defaultWidth=defaultWidth ;
    this.toggleDisplay = function() {
        this.displayed = (this.displayed==="none") ? "" : "none";
    }
}

myApp.Notepad = function(defaultFont) {
    this.writeable = true;
    this.font = defaultFont;
    this.setFont = function(theFont) {
        this.font = theFont;
    }
}

myApp.notepad1 = new myApp.Notepad('helvetica');
//Without prototype we can only kluge inheritance here. Example (5) will fix it.
myApp.notepad1.defaultWidth; //undefined

5. Prototype with Constructor for new

//utility function
function deepClone(obj) {
    var clone = {};
    for(var i in obj) {
        if(typeof(obj[i])==="object") {
            clone[i] = deepClone(obj[i]);
        } else {
            clone[i] = obj[i];
        }
    }
    return clone;
}
 myApp = {};

myApp.Panel = function(defaultWidth) {
    this.defaultWidth = defaultWidth;
}

myApp.Panel.prototype.toggleDisplay = function() {
    this.displayed = (this.displayed==="none") ? '' : "none";
    alert('display = ' + (this.displayed ? 'on' : 'off'));
}

myApp.Notepad = function(defaultFont,defaultWidth) {
    myApp.Panel.call(this,defaultWidth); //inject self into Panel constructor
    this.font = defaultFont;
}

//inherit from Panel....
//better to simply grab Panel's prototype rather than create new instance of Panel
myApp.Notepad.prototype = deepClone(myApp.Panel.prototype);

myApp.Notepad.prototype.writeable = true;
myApp.Notepad.prototype.setFont = function(theFont) {
    this.font = theFont;
}

//true inheritance - this time using prototype
myApp.notepad1 = new myApp.Notepad('helvetica',300);
myApp.notepad1.defaultWidth; //300
About these ads

9 thoughts on “Five ways to create objects – part 2: Inheritance

  1. If you can’t find valid uses for OO in client-side Javascript, it could be that you just aren’t thinking in OO constructs enough to understand how they can be useful to you. I use inheritance all the time to turn JSON data into working objects (without the dangerous eval). Remember, JSON is just a subset of object literal notation, so you can inherit from JSON objects to create your active instances:

    Step 1: Grab some JSON from the wire:

    var fruit = incomingJSON;

    Step 2: Use prototype to inherit the properties of fruit (using the recently standardized Object.create):

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

    fruit = Object.create(fruit);

    Now that fruit has been blessed with new, it gains access to the prototype property, which can be used for patterns like compositional inheritance. For example, you could endow the object with properties and methods that didn’t come over the javascript wire, using a client-side modeling system. Let’s make sure that fruit complies with our model for food:

    fruit = model.food.hydrate(fruit); // hydrate an existing food model using data passed in the fruit object.

    Your hydrate method can pull double-duty as a security check and prevent data injection attacks and other insanity.

    To further simplify, you can embed the Object.create into your hydrate method, so all you have to do when you grab JSON from the wire is:

    myObject = model.objectType.hydrate(incomingJSON);

    And like magic, you have the data from the wire merged together (using prototype-based compositional inheritance) with all the standard default properties and methods associated with your model. Of course, it saves your data transport bandwidth (you only have to send non-default data), and your hydrate() sanity checks can provide you with added security, to boot.

    • Eric – you illustrate a very cool usage. I’m playing devil’s advocate somewhat because I generally see folks are too trigger happy when it comes to creating long and hard to decipher inheritance chains in JavaScript because its what they were taught to do in C++/Java etc. JavaScript presents plenty of alternatives to inheritance and often they are cleaner and more transparent (lambda immediately springs to mind)

      Also to be clear I use OO all the time in JavaScript – and I use inheritance implicitly every time I refer to a prototype (which is very often). I’m just not wild about building up complex hierarchies on the client.

      Thanks again for the cool example

    • @Eric
      Thanks for the useful example of augmenting an object onto a new less specific object by way of its prototype.
      It kind of looks a bit back to front, as the prototype is more specific in your example, rather than less specific (abstract)?
      This may be my classical background shinning through, but shouldn’t the prototype be less specific?
      Or have I misunderstood what your doing here?
      Other than that, something along these lines could get some use in my tool kit.
      Cheers.

  2. I agree. I’ve encountered few problems for which complex hierarchies are the best answer, yet there is disproportionally much time invested in simplifying the use of javascript for those instances.

    OO is much more than just inheritance.

  3. The deepClone method will fail (run out of stack space) if any object points to itself directly or indirectly. It should leave breadcrumbs to detect loops and then remove them.

    Also, cloning a prototype does not provide true inheritance. It just copies properties and methods, leading to duplication (of storage), is slow, and is not dynamic (changes to the prototype of the super class are not reflected in the subclass).

    To share methods and properties (which is the aim of inheritance), the subclass’s prototype should be set to an instance of the super class.

    E.g.

    // Create the super class
    var SuperClass = function() { }; // Constructor
    SuperClass.prototype.hello = function() { return 'Hello'; }; // Method

    // Create the class that inherits the super class
    var SubClass = function() { }; // Constructor
    SubClass.prototype = new SuperClass(); // Extends SuperClass
    SubClass.prototype.world = function() { return 'World'; }; // Method

    // Object with inheritted methods
    var obj = new SubClass();

    // Can change super class properties
    SuperClass.prototype.exclamation = '!';

    alert(
    obj.hello() + // Method from SuperClass
    ', ' +
    obj.world() + // Method from SubClass
    obj.exclamation // New property from SuperClass (changed after this object was created)
    ); // Displays: Hello, World!

  4. @Angus
    Are you sure “brute-force inheritance” is inheritance?
    To me it looks a lot more like aggregation than inheritance?
    Not that aggregation is a bad thing, in fact quite the contrary.

    Re: example 3.
    Where can we find Doug Crockfords “Constructor using Object Literal”?
    Do you have a link?
    This appears to be contrary to Doug Crockfords statement:
    “If a constructor is called without the new prefix, very bad things can happen”.
    I guess he wrote this example after the above statement.
    Thus using “that” rather than “this” negates that problem.
    This is an elegant example.

    Re: example 5.
    Does “myApp = {}” need a var prefix?
    “Uncaught ReferenceError: myApp is not defined” in strict mode.

    I think this may have been what example 3 was trying to achieve.
    This looks really good Angus!

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