Jimmy Breck-McKye

A lazy programmer

Prototypical Inheritance Is Unsafe

If you rely on prototypes for implementing inheritance in JavaScript, you are putting yourself at risk of shared state bugs. That’s because the shared nature of prototypes means that any assignments to reference values (arrays or objects) or side effects in closures (like those used to emulate private variables in JavaScript) will be shared amongst all instances of children.

This is probably easier to demonstrate than to explain. Let’s start with a simple parent ‘class’:

Prototypical inheritance
1
2
3
4
5
6
7
8
9
10
11
12
13
function Parent() {
    var myPrivateVariable = false;

    this.flipPrivateVariable = function() {
        myPrivateVariable = true;
    };

    this.getPrivateVariable = function() {
        return myPrivateVariable;
    };
}

Parent.prototype.myArray = [];

So our Parent object has a private variable, myPrivateVariable, that sits in the closure of the constructor function. Privileged methods in the same scope have the ability to manipulate it. Instances of Parent also come with an array, myArray. So far, so straightforward. Let’s inherit from it via prototype:

Prototypical inheritance
1
2
3
4
5
6
7
8
function Child() {
    // what goes in here doesn't matter
}

Child.prototype = new Parent();

var bob = new Child();
var mary = new Child();

So we have two new child instances, Bob and Mary. This seems fine so far. But danger awaits:

Prototypical inheritance
1
2
3
4
5
bob.flipPrivateVariable();
mary.getPrivateVariable(); // returns TRUE

mary.myArray[0] = 'I am Mary';
bob.myArray.pop(); // returns 'I am Mary'

Flipping the private variable on Bob has changed the private variable on Mary. And adding elements to Mary’s array has changed Bob’s. Try it in the console if you like.

Why it happens

Bob and Mary both have a delegate object, their prototype, which was set on the Child constructor. That object is shared between them. Therefore, there’s only one closure around the privileged methods on that object. There’s only ‘one’ flipPrivateVariable and it’s flipping only one private variable. So if you expect your child objects to inherit a parent’s handling of private state, you’re going to be disappointed.

As for the array access, this happens because when you set elements on the array, you’re not shadowing the property. Normally, assignments to prototype properties causes them to be overwritten with a value ‘local’ to the specific object. But you’re not overwriting anything when you access the array members – you’re adding and overwriting the array’s members. This happens with anything implemented as a reference value – and that includes plain objects.

Why this is insidious

The problem with this behaviour is that it’s unintuitive, and won’t raise bugs until you have more than one instance of an object. This might happen quite late in a project, so even if the enhancement is raised with the original developer, they’ll be trawling through unfamiliar code. And the refactor they’ll have to do will be much harder as a result.

What can I do about this?

There are a couple of obvious solutions.

Keep using prototypes, but keep reference values off prototypes and re-apply constructors

The solution is twofold:

  1. Don’t attach any reference values (arrays, objects) to prototypes. Make sure everyone on the team knows about this behaviour, and police your organization’s JavaScript with code reviews (easier said than done)
  2. Use function.apply(this) to call the parent constructor in the body of the child constructor. This will create a new closure and a new set of privileged methods within the closure of the child object, so that are unique to the instance. This technique is documented by Ben Nadel.

So for our code, this would mean:

Prototypical inheritance with caveats
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function Parent() {
    var myPrivateVariable = false;

    this.flipPrivateVariable = function() {
        myPrivateVariable = true;
    };

    this.getPrivateVariable = function() {
        return myPrivateVariable;
    };

    this.myArray = [];
}

Parent.prototype = {
    somePublicField : 'foo'
};

function Child() {
    Parent.apply(this); // calls the parent function, passing a value to use as 'this'.
                        // Parent will decorate the Child's 'this' value, so it will
                        // have its own copy of flipPrivateVariable / getPrivateVariable,
                        // and its own unique closure too, created by running Parent()
                        // once again
}

Child.prototype = new Parent(); // anything added by Parent.apply(this) will potentially
                                // shadow fields added on this prototype

var bob = new Child();
var mary = new Child();

bob.somePublicField; // returns 'foo'

bob.flipPrivateVariable();
mary.getPrivateVariable(); // returns false

bob.myArray.push('I am Bob');
mary.myArray.pop(); // returns undefined

It works, but I think it’s a bit awkward myself. You have to warn your peers not to shove reference values onto prototypes, or as public fields of anything they might wish to later extend. Maybe this is feasible if you’re a small team, but I wouldn’t take my chances. As I’ve explained above, if one misuse gets through (easy when the bug is so subtle), you might not spot the problem until a point where refactoring will be much, much harder.

Ditch the prototypes, and just use parasitic inheritance

This is my favoured solution. We lose the prototypes altogether and use a much simpler approach to inheritance – parasitic constructors.

Parasitic inheritance
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function Parent() {
    var parent = new Object();

    var myPrivateVariable = false;

    parent.flipPrivateVariable = function() {
        myPrivateVariable = true;
    };

    parent.getPrivateVariable = function() {
        return myPrivateVariable;
    };

    parent.myArray = []; // we put this in the closure, not on a shared prototype

    parent.somePublicField = 'foo';

    return parent;
}

function Child() {
    var child = new Parent(); // you could also use Parent.apply(this), but this way
                              // you automatically avoid using 'this', which has problems
                              // with binding

    child.foo = 'foo';

    return child;
}

var bob = new Child();
var mary = new Child();

bob.somePublicField; // returns 'foo'

bob.flipPrivateVariable();
mary.getPrivateVariable(); // returns false

bob.myArray.push('I am Bob');
mary.myArray.pop(); // returns undefined

Every instance of child has its own instance of parent, which it decorates with its own fields and finally returns. With no shared parent objects, there can be no shared state bugs.

These functions act a lot like normal constructors (you can call them safely with the new operator), although they do lose you the ability to use instanceof to determine an object’s class family. You can still use instanceof to determine Bob and Mary’s primitive types (string, object, function, etc.), but not whether they’re an instance of Parent. Personally, I’ve probably only ever used instanceof a handful of times – in most projects I don’t deal with objects of unknown pedigree, and I prefer duck-typing when I do anyway.

The other caveat is that because all the properties are duplicated, parasitic inheritance can potentially be memory inefficient if there are lot of large fields. This isn’t normally the case unless you have a lot of objects with complex methods, however. If really needs be, it might be ameliorated by putting large functions on the Parent into some kind of closure surrounding it, and calling them by delegates. For example:

A more memory-efficient approach
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var Parent = (function(){
    // we're using an IIFE to provide a closure for any big functions 
    // or STATIC fields that might live on Parent

    function somethingComplexAndHorrible(thisValue, options) {
        // The main body of the method lives here, and we pass it a 'this' value to work on
    }

    return function Parent() {
        var parent = new Object();

        var myPrivateVariable = 'false';
        parent.myComplicatedMethod = function(options) {
            somethingComplexAndHorrible(parent, options);
            // this method gets duplicated, but it's quite small,
            // so the impact isn't too severe
        };

        return parent;
    };

})();

function Child() {
    var child = new Parent(); // Parent() gets called as normal    
    return child;
}

You should only bother trying this if really necessary, though. We all know that premature optimization is the root of all evil

In any case, parasitic inheritance is my favoured approach, because it requires a lot less education and policing than trying to use prototypes safely. There seems to have been an obsession with prototypes in the front end community as of late, amounting to something of a war on constructors, but few of the discussions out there seem to recognize the exact nature of prototypes and what they mean for extensible classes with private state. Make sure that you and your team, however, do.

Comments