jneens web site

Creating Uninitialized Objects in Javascript

When I first started writing larger applications in Javascript, I was stumped about how to set up a prototype chain correctly. The idiomatic way runs code that isn’t strictly necessary:

function BaseClass(thing) { this.thing = thing; }
function SubClass() { BaseClass.apply(this, arguments); }
SubClass.prototype = new BaseClass;

The problem here is that the third line runs the constructor function with no arguments. In this case it’s fine, because it sets SubClass.prototype.thing to undefined. But what if the constructor has to do some kind of computation, or has required arguments, or (gasp) does I/O? Not the best coding practices in the world, sure, but I’ve worked with this kind of code.

In modern JS environments, the right thing to do is use Object.create:

SubClass.prototype = Object.create(BaseClass.prototype);

But if you want to support older browsers, a different strategy is called for.

It turns out, the Javascript runtime has no way of telling which constructor was used to create an object. Its instanceof operator only looks at the prototype chain. In environments supporting Object.getPrototypeOf we could implement instanceof ourselves:

function isInstanceOf(obj, constructor) {
  if (typeof constructor !== 'function') throw new TypeError;
  if (typeof obj !== 'object') throw new TypeError;

  // look up the prototype chain for a match
  while (true) {
    if (obj === constructor.prototype) return true;
    if (obj === null) return false;

    obj = Object.getPrototypeOf(obj);
  }
}

So in particular, we can use a completely different constructor to make instances of the same function!

function f() {}
function g() {}
f.prototype = g.prototype = {a: 1}
(new f) instanceof g; // => true
(new g) instanceof f; // => true

So in order to make a new “instance” for subclassing, we can just initialize an empty constructor that has the right prototype, and the runtime can’t tell the difference.

function create(_super) {
  function noop() {}
  noop.prototype = _super;
  return new noop;
}

SubClass.prototype = create(BaseClass.prototype);

Coffeescript uses this approach when it needs to construct objects with varargs. We’ve used a variation of this approach in the latest version of pjs. Pjs classes carry around ready-made noop constructors with the correct prototypes already set. For a pjs class, if you want an empty instance of MyClass, just type new MyClass.Bare.

Happy coding!

EDIT: Simplified the isInstanceOf implementation

– Jeanine

(discussion on HN)