Skip to content

Instantly share code, notes, and snippets.

@travisperson
Forked from caseman/gist:3428752
Created August 23, 2012 20:13
Show Gist options
  • Save travisperson/3441085 to your computer and use it in GitHub Desktop.
Save travisperson/3441085 to your computer and use it in GitHub Desktop.
Ctor: Lightweight Javascript Constructors with Inheritance

Ctor: Lightweight Javascript Constructors with Inheritance

Author: Casey Duncan @iamnotcasey

This Javascript constructor with inheritance pattern is designed as a lightweight alternative to other methods I've seen while still providing a nice abstraction as a 13 line function that can be easily inlined into your code.

A big difference using Ctor vs. other popular inheritance solutions is its use of a "member init" function, instead of an object literal to define the members of the prototype. I personally find using object literals awkward and unwieldy for defining member methods. The function also gives you a closure which makes private definitions, and caching values in the constructor convenient and easy. Ctor passes the "member init" function the parent prototype, so it can easily call methods on it when extending a parent method. This provides super-like functionality simply and without undue magic.

One important thing about Ctor is that it is not doing anything other than straightforward prototype inheritance. There is no universal base "class", and in fact it isn't an attempt at class-based inheritance at all. The resulting constructors are normal Javascript functions. It also avoids using any ES5-specific functionality like Object.create().

This implementation of Ctor requires the use of new with the resulting constructors, but this could be avoided with a few more lines of code. I know some folks hate new with a passion, I myself find it can aid readability, but of course you can decide for yourself.

This code is released under the MIT license. Feel free to use it as you see fit. If you see ways to improve it, leave a comment. Enjoy.

function Ctor(Parent, memberInit) {
  if (!memberInit) {
    memberInit = Parent;
    Parent = function(){};
  }
  var F = function(c) {
    if (this.init && c !== Ctor) {
      this.init.apply(this, arguments);
    }
  }
  memberInit.call(F.prototype = new Parent(Ctor), Parent.prototype);
  return F;
}

//// Examples ////

// With only one arg, Ctor creates a base constructor
// The "member init" function is called with `this` as the constructor
// prototype for convenient manipulation.
var Base = Ctor(function() {
  this.toString = function() {
    return this.first + ' ' + this.last;
  }
});

var Duncan = Ctor(Base, function() {
  this.last = 'Duncan';
});

var Casey = Ctor(Duncan, function() {
  this.first = 'Casey';
});

// If you provide an `init` member function, it will be called
// at construction passing the arguments given to the constructor.
// `this` is bound to the new instance inside `init()` as you would expect.
var Anyone = Ctor(Duncan, function() {
  this.init = function(first, last) {
    this.first = first || this.first;
    this.last = last || this.last;
  }
});

// The "member init" function to Ctor is passed the parent constructor's
// prototype for convenience. This allows you to very easily invoke
// methods of the parent when inheriting.
var Middle = Ctor(Anyone, function(sup) {
  this.init = function(first, middle, last) {
    sup.init.call(this, first, last);
    this.middle = middle;
  }
  this.toString = function() {
    return this.first + ' ' + this.middle + ' ' + this.last;
  }
});

function log(what) {
  console.log(String(what));
}

log(new Casey); // => 'Casey Duncan'
log(new Anyone('Lynelle')); // => 'Lynelle Duncan'
log(new Anyone('Robert', 'Fripp')); // => 'Robert Fripp'
log(new Middle('Ronnie', 'James')); // => 'Ronnie James Duncan'
log(new Middle('Ronnie', 'James', 'Dio')); // => 'Ronnie James Dio'
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment