Classes and Instantiation Patterns in javaScript

July 5, 2015

Any construct that is capable of producing a fleet of similar instances conforming to some interface can be called a Class.

Without entering into the debate about the OOp aspects of the javaScript language specific to Classes, and if classes/instances truely exists in js, let’s just rely on the above definition, and explore the different patterns available to us for defining Classes and instantiating them.


In general

Every pattern need to take care of the basics:

  • Create a new object
  • Assign some properties and methods to it
  • Return the new decorated object

The Functional pattern

It does just what is suppose to do:

var FunctionalPattern = function(){
  // Create a new object
  var instance = {};

  // Add methods to it
  instance.aMethod = function(){};
  instance.anotherMethod = function(){};

  // Return the new decorated object
  return instance;
};

// Instantiation Pattern:
var instance = FunctionalPattern();

Pros & Cons

The functional pattern have the advantage of returning methods that were defined within the constructor’s lexical scope, therefore creating closures (and making the object constrution pretty clear). The very same principle that give the functional pattern the closure advantage also define its drawback. As the methods are declared within the the lexical scope of the constructor, instantiating a class via the functional pattern will result in duplicating all its methods, meaning new functions stored in new spots in memory.


The Functional-shared pattern

Born as an effort to limit the drawbacks of the functional pattern, it moves the methods declaration outside the lexical scope of the class, extending it methods by reference. In this way each istance will have pointers to the same functions without duplicating them:

var FunctionalSharedPattern = function(){
  // Create a new object
  var instance = {};

  // Add methods to it
  instance.aMethod = functionalSharedMethods.aMethod;
  instance.anotherMethod = functionalSharedMethods.anotherMethod;

  // Return the new decorated object
  return instance;
};

// shared methods where each instance will point to
var functionalSharedMethods = {
  aMethod: function(){},
  anotherMethod: function(){},
};

// Instantiation Pattern:
var instance = FunctionalSharedPattern();

…definitely a WET solution.

WET: Write Everything Twice or We Enjoy Typing

With the help of a little helper we could refactor the functional-shared pattern to be a little more DRY instead

DRY: Don’t Reapeat Yourself

var FunctionalSharedPattern = function(){
  // Create a new object
  var instance = {};

  // Add methods to it
  extend(instance, functionalSharedMethods);

  // Return the new decorated object
  return instance;
};

// shared methods where each instance will point to
var functionalSharedMethods = {
  aMethod: function(){},
  anotherMethod: function(){},
};

// Basic Extend Helper
function extend(instance, methods) {
  for (var key in methods) {
    if(!instance.hasOwnProperty(key))
    instance[key] = methods[key];
  }
}

// Instantiation Pattern:
var instance = FunctionalSharedPattern();

Pros & Cons

The functional-shared pattern solve the cons we saw in the functional pattern. But, it still have a big drawback: the pointers in each instance are created during the instantiation only, meaning that if we add or edit shared methods those won’t be available to our instances unless we update them (i.e. by invoking the extend helper again on each of our instances.).


The Prototypal pattern

By now you probably noticing a trend in how this blog post is structured, we are building up a better pattern to solve the drawbacks of the previous one. And this is where the Prototypal pattern come handy. By relying on the prototypal delegation on which javaScript objects obey, it provide a handy way to keep in sync any istance with its class. Long story short: the prototypal pattern allow us to edit, add and remove methods on the Class and automagically have each istance updated accordingly.

var PrototypalPattern = function(){
  // Create a new object
  var instance = Object.create(PrototypalPattern.prototype);

  // Methods will be added to the prototype

  // Return the new decorated object
  return instance;
};

// PrototypalPattern will delegate to its prototype for methods lookup
PrototypalPattern.prototype.aMethod = function(){};
PrototypalPattern.prototype.anotherMethod = function(){};

// Instantiation Pattern:
var instance = PrototypalPattern();

Pros & Cons

There are’n much drawbacks with this Pattern, as it solve all our issues we encountered sofar. So, maybe there is not a better way, but just a sweeter way…


Pseudoclassical

Sometime an advantage could be given by the language itself and its syntactic sugar, and guess what, mr. Pseudoclassical pattern is very very sweet:

var PseudoclassicalPattern = function(){
  // Create a new object
  // var this = Object.create(PseudoclassicalPattern.prototype)
  // is automatically done by the interpreter when the function is
  // called with the constructor call

  this.aMethod = function(){};
  this.anotherMethod = function(){};
}

// Instantiation Pattern:
var instance = new PseudoclassicalPattern();

Final thoughts and performance

The clear question we should all ask our self is: which pattern should we use? As for everything the answer is: it depends. It depends on the codebase you are working on and which pattern is most used there, in that case will be a good idea to stick to that. For example the Pseudoclassical is the preferred pattern in MVC’s such as backbone. Another interesting aspect to take into consideration when deciding which pattern to use could be related to performance, but this is a subject for a another blog post…

Thanks to:
Hack Reactor for being awesome.
Charles Crame for helping in proof reading this post.
Ryan Atkinson for his inspirational blog.

comments powered by Disqus