Prototypal Master Class, Crockford

© 2011, Martin Rinehart

Douglas Crockford is, by any account, one of today's leading JavaScripters. He is the author of the best JavaScript style/code checker, JSLint; curator of JavaScript Object Notation, aka JSON; and author of JavaScript: The Good Parts which will be several printings and translations past its impressive current statistics by the time you read this. Most important, for our present purpose, his prototypal object extensions are the purest and the simplest you will find today.

In case this is your first exposure to your author's biases, I love simple code. Simple is usually better than thorough. Great code is simple, readable code. Code that shows how "clever" the author is should be cause for firing the author. Let's begin with first principles.

The Prototypal Principle

The basic idea of prototypal inheritance is that objects inherit from other objects, not from a class. Crockford tried class-based inheritance in JavaScript, Classical Inheritance in JavaScript, and decided he didn't like it. He was back to pure prototypal when he wrote Prototypal Inheritance in JavaScript and he has stayed there since.

The basic Crockford idea, indeed the fundamental prototypal idea, is that a new object uses an existing object as its prototype. Unwrapped, all of Crockford's methods look like this:

function Constructor( params ) { /* extra code here */ }
Constructor.prototype = your_prototype_object;

var instance = new Constructor( . . . );

A purist would argue that the above constructor does not need code, since the object it constructs will have all the properties of the prototype object. (Software purists are, fortunately, rare. Software is here to serve you. You are not here to serve the software.) With the fundamentals in mind, here are Crockford's prototypal methods from oldest to most recent.

Crockford's Prototypal Methods

We're ignoring Crockford's class-based methods. They are important, as he points out, in proving that we could do class-based work in JavaScript, if we wanted to. They are not otherwise useful. Prototypal inheritance must be substantially dumbed down to reduce it to class-based inheritance.

Original Crockford

Of this method, dated June, 2006, Crockford says, "It is a standard feature in my toolkit, and I highly recommend it for yours."

Within the object() function—a global function that returns an object but is not a constructor—Crockford creates a new, empty constructor, F. He assigns an object which is passed in as the argument to object() as F.prototype. Then he returns a new F():

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

Remember that new F(), in spite of its brevity, does what every use of the new operator promises. It creates an initially empty object; it assigns a property named constructor to the object and it assigns an implicit property that is a reference to the constructor's prototype property (in this case, F.prototype which has been set to your calling object, o.). Finally it returns that object reference (usually seen as the this reference inside the constructor). The return statement here passes that object reference out to the outside world.

If we add a line making use of this function, we have the fundamental prototypal idea as noted above:

var instance = object( your_prototype_object );

The line above creates instance which is a reference to an object which has your_prototype_object set as its prototype.

First Revision

In the same article Crockford provides an addendum dated April, 2007, in which he simply states, "Here is another formulation:"

Object.prototype.begetObject = function () {
    function F() {}
    F.prototype = this;
    return new F();
};

newObject = oldObject.begetObject();

This is almost identical to the first, except that he adds the new function, now called begetObject() to the Object constructor. This means that any object can call any_object.begetObject() and within the begetObject() method, this will be the calling object (any_object, in our example).

Crockford points out that this formulation no longer includes an object() function, a global object in the first version.

Second Revision

A year later, Crockford adds a third formulation. Instead of adding code to Object.prototype, where any object can call it as its own method, he adds a method property, create() directly to Object. You no longer call any_object.create() (to use the new name in the older formulation) but you call Object.create( any_object ).

The more substantive change is that Object is checked for an Object.create() and the new one is not added if one already exists. (This works well if you assume that no other library file is adding an Object.create() that addresses some other issue.)

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

Crockford is a member of ECMA TC 39 (Technical Committee 39, the group responsible for the ECMA 262 standard) where he would get to talk to the engineers from Apple, Google, Microsoft, Mozilla, Opera and others. The first version above creates a new function every time you create an object instance. The later versions just check for the existence of the object-creating function, only creating it the first time you create an object instance. One assumes that the overhead of creating the function was material.

The Good Parts Revision

In his The Good Parts, copyright 2008 so it is not clear whether the book or the formulation above was later, Crockford presents this nearly identical version:

if (typeof Object.beget !== 'function' ) {
    Object.beget = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}
var another_stooge = Object.beget(stooge);

(A stooge object had been created earlier as part of a running example in the book.)

All of these, albeit in slightly different ways, take an existing object, assign a reference to it as the value of the prototype of a constructor and use the constructor to create a new instance object. By doing so, the new instance will have its implicit reference to its prototype pointing to the existing object. (More exactly, the implicit reference will be pointing to the constructor's prototype property, which itself is a reference to the existing object.) The existing object is the prototype of the new instance object, the basis of prototypal inheritance.

Example

For each of the articles in this series we look at the code needed to create three objects, each having two properties of its own and a toString() method appropriate to all its properties. The second object extends the first; the third extends the second. Each extending object uses its extended object's toString() as part of its own toString() showing method overriding and access to overridden methods. Objects report the class of which they are instances, if applicable.

Application Code

var o0 = {
        a: 0,
        b: 1,
        toString: function () {
                return '{a=' +
                this.a + ',b=' +
                this.b + '}';
        }
    };

var o1 = Object.create( o0 );
    o1.c = 2;
    o1.d = 3;
    o1.toString = function () {
            return '{' + o0 +
            ',c=' + this.c +
            ',d=' + this.d +
        '}';
    };

var o2 = Object.create( o1 );
    o1.e = 4;
    o1.f = 5;
    o2.toString = function () {
            return '{' + o1 +
            ',e=' + this.e +
            ',f=' + this.f +
        '}';
    };

alert( o0 ); // {a=0, b=1}
alert( o1 ); // {{a=0, b=1}, c=2, d=3}
alert( o2 ); // {{{a=0, b=1}, c=2, d=3}, e=4, f=5}

Library Code

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

Critique

There are two comments I want to make, but first let me say again that I love simple code and I agree with Crockford completely that prototypal inheritance is far more powerful and flexible than class-based inheritance. You can readily emulate Java in JavaScript. The reverse would be like building a house with just a hammer and a hand saw.

Avoiding Globals

One question I have is on the replacement of the global function object with either a property of Object or a property of Object.prototype.

If the creation of a global function is not desirable (and I agree that it is not) does it therefore follow that the creation of a new property of a global function is in some way superior? I don't see how this is an improvement. It may be true that Object.create is less prone to accidental collision with an identical name than is a function named object. If this were the goal, certainly Object.prototype.begetObject() is the best choice. Still, there are better ways to guard against name collisions.

Scrubbing Objects

If you are not creating more comic actors, using one object as the prototype of new objects implies that you give some thought to the prototype. If it is a prototype person, do you want the prototype to have a first name, for example, or do you want an empty string for the prototype's first name? I would go with the empty string.

This means that you will choose between these alternatives:

var person_prototype = { first_name: '', last_name: '', . . . };
var new_person = Object.create( person_prototype );
    new_person.first_name = 'Actual';
    new_person.last_name = 'Name';

The above begins by taking a person object, scrubbed of values unsuitable for use as a prototype. Compare it to:

function Person( first_name, last_name, . . . ) {
    this.first_name = first_name;
    this.last_name = last_name;
    . . .
}
var new_person = new Person( 'Actual', 'Name', . . . );

I do not find a strong argument for one form over the other.

Moving On

We've looked at the simplest alternative first. In order of complexity, we also have these choices:

You decide if the extra power is worth the extra complexity.


Feedback: MartinRinehart at gmail dot com

# # #