Alexander Dickson

JavaScript's prototype property

Alright, you’ve been doing object oriented stuff in languages with a classical object implementation. Now you want to do something similar in JavaScript, and who could blame you? But be warned, prototypical inheritance is a little different.

The type of your proto

Each object in JavaScript has a special reference called [[Prototype]] to another object (except for the end of the road, Object.prototype, which has a null prototype) of whom is the prototype of that object. This special link has only recently been easy to access thanks to the originally non-standard __proto__ property or Object.getPrototypeOf().

The purpose of this special link is to allow object delegation - inheritance, if you must. When a property lookup is performed on an object ([[GetProperty]]), the runtime will check the receiver object first, and if it does not contain that property, it will follow the [[Prototype]] link to an other object, and so forth until it finds a property or bubbles up all the way to Object.prototype, which has a null [[Prototype]], returning undefined.

For example, you may have found you can call certain methods on any Object, for example, the hasOwnProperty() method. Let’s see how this works…

/(?:)/.hasOwnProperty("source");
  1. Pardon me RegExp object, do you have a method called hasOwnProperty?
  2. Nah mate, but let me follow my [[Prototype]] link to RegExp.prototype.
  3. Got it?
  4. Not yet, hold ye horses! Let’s follow the RegExp.prototype’s [[Prototype]] link to Object.prototype.
  5. C’mon, do you have it yet?
  6. Certainly do! Let’s execute it with its this context set to the original RegExp object.

Remember that setting a property doesn’t walk the [[Prototype]] chain, it always sets on the receive object directly.

How does one specify the prototype?

When creating a constructor function, one can specify where the prototype property points to.

var Foo = function() {
    // ...
}

Foo.prototype = {
    method: function() {
        // ...
    }
}

var a = new Foo;

a.method();

The new operator was introduced to make JavaScript look a bit friendlier to people coming from a classical OO background. When using the new operator in conjunction with a constructor function, the performs a few steps…

  1. Creates a new object (an instance, if you’d prefer, of the constructor function).
  2. If the prototype property (not the [[Prototype]]) points to an object, point the [[Prototype]] to it, otherwise, point it to Object.prototype.
  3. Call the constructor function passing in the arguments and setting its this to the new object.
  4. If the return value of the constructor is an object, return that. If not, ignore return value and use this.

By default, the prototype property is an empty object. I often use the pattern above, assigning a new object to prototype, rather than augmenting the original, to save me writing out prototype a bunch of times. The prototype object exists once for each function (just in case it will be used as a constructor), that is why you can modify the prototype object and immediately have any past, present or future objects instantly have access to the changed prototype.

Note that this is not the [[Prototype]] being directly set. The [[Prototype]] link and the prototype property are not the same thing, but as you can probably now tell are very closely related.

How can we do inheritance with this?

Observe…

var Bar = function() {
    // ...
}

Bar.prototype = new Foo;

var b = new Bar;

// Calls `method` that was defined on Foo's `prototype` object.
b.method();

There are a number of problems with this way of specifying an object’s prototype

  • You need to instantiate the other object being pointed to which may have unintended side effects.
  • No out-of-the-box method to pass arguments to the parent’s constructor when instantiating the child (you need to throw a Parent.apply(this, arguments) in each constructor).
  • It’s kinda ugly, and doesn’t really have a place in a language which has prototypical inheritance.

So there’s a better way, right?

‘Course! You can use Object.create() to set up [[Prototype]] chains. This method’s first argument is the object to point the [[Prototype]] to and the second argument is a list of properties for the created object (but we won’t be covering that argument today).

var b = Object.create(Foo.prototype);

// Calls `method` that was defined on Foo's `prototype` object.
b.method();

A better example, ignoring the context of all previous examples is…

var model, page, about;

model = {
    init: function(properties) {
        this.properties = properties || {};
        return this;
    },
    get: function(key) {
        return this.properties.hasOwnProperty(key) ? this.properties[key] : 
               this.defaults.hasOwnProperty(key) ? this.defaults[key] : null;
    },
    set: function(key, value) {
        this.properties[key] = value;
        return this;
    },
    toString: function() {
        return Object.keys(this.properties).map(function(key) {
            return encodeURIComponent(key) + '=' + 
            encodeURIComponent(this.properties[key]);
        }, this).join("&");
    },
}

page = Object.create(model);

page.defaults = {
    title: "Untitled"
};

about = Object.create(page).init().set("contents", "About me!");

document.title = about.get("title");
document.body.textContent = about.get("contents") + " ";
document.body.innerHTML += "Permalink".link("page?" + about);

This example creates an object called model (similar to Backbone’s Backbone.Model), an object called page which inherits from model, and finally an about object that inherits from page.

Because using Object.create() doesn’t rely on a constructor function, it’s handy to choose a method name to use as a constructor for your object’s setup (if required). In my example above, I chose init().

Why do people still muck around with new everywhere?

Old habits die hard, the new operator isn’t a problem per se, and Object.create() is not implemented in all browsers, namely older IEs. A polyfill is relatively simple to support the case of creating new objects (the second argument’s functionality can not be polyfilled in these browsers that don’t support Object.defineProperty()).

Object.create = Object.create || function(parent) {
   if (arguments.length > 1) {
       throw new Error("This implementation of Object.create only supports the first argument.");
   }
   
   function F() { }
   F.prototype = parent;
   return new F;
};

Now you’re talking, and now it looks a lot more like prototypical inheritance and no sign of new anywhere, except for in this polyfill.

Which one should I use?

Up to you, but I’d recommend giving Object.create() a try. It is easy to polyfill in the browsers that don’t implement it.


Want to discuss this post? Just mention me @alexdickson.