«

JavaScript Module Patterns

Hey. Let's talk about JavaScript module patterns. They're a lot of fun.

Prototype Pattern

function Person(name) {  
  this.name = name;
}
Person.prototype.sayName = function() {  
  console.log('My Name is ' + this.name);
}
var person = new Person('Joel');  
person.sayName(); // 'My Name is Joel'  

Probably the most widely used pattern is the Prototype pattern. Prototypes are JavaScripts way of handling inheritance, and without going too much into that, it's the built-in way for objects to share and inherit methods.

There are some great reasons to use the Prototype pattern:

Some issues arise, however, because of the keyword this.

// Setup a EventHandler
function EventHandler(firedText) {  
  this.firedText = firedText;
}
EventHandler.prototype.handleClick = function(e) {  
  console.log(this.firedText);
}
var eventHandler = new EventHandler('fired!');  
window.addEventListener('click', eventHandler.handleClick);  

In the funciton constructor, we accept a string firedText that we attach to this. However, when we attach the event to the window object, the this reference changes from being a reference to EventHandler to now being a reference to window. There is a way around this behaviour, however it's only supported in ES5 browsers:

// Setup a EventHandler
function EventHandler(firedText) {  
  this.firedText = firedText;
}
EventHandler.prototype.handleClick = function(e) {  
  console.log(this.firedText);
}
var eventHandler = new EventHandler('fired!');  
window.addEventListener('click', eventHandler.handleClick.bind(eventHandler));  

.bind is a subject for another post, but in short it makes the this reference whatever you pass into it, which in this case is eventHandler as we previously had assumed.

Closure Pattern

function Person(name) {  
  function sayName() {
    console.log(name);
  }
  return {
    sayName: sayName
  };
}
var person = new Person('Joel');  
person.sayName(); // Joel  

The closure pattern operates by utilizing closures (shockingly), or simply the fact that variables referenced outside a function (and are used IN that function) "hang" around after context passes. In this case, the name argument is still present even after we've executed the Person constructor.

Some nice things about this pattern:

However this comes at a cost: no prototypes. Since the object we return is the instance object, there isn't a great place to append prototype methods. Also, this method of modularizing can be dangerous when generating and keeping track of state as all our references are in a closure (and thus hard to nullify).

Object Literal Pattern

var person = {  
  setName: function(name) {
    this.name = name;
  },
  sayName: function() {
    console.log(this.name);
  }
};
var joel = Object.create(person);  
joel.setName('Joel');  
joel.sayName(); // 'Joel'  

Sharing some traits with prior patterns, the object literal pattern uses an ES5 method Object.create which accepts takes an object and create's a new one, with the object argument being set as it's prototype. This is, essentially, a direct replacement for using the new keyword to create an instance.

Obviously, there are some drawbacks, namely the this keyword being present inside methods. Because of that, there be issues using this pattern when assinging events on the window object.

This also is somewhat confusing to folks transitioning from classical languages, as there is no new to be found. However, this is more-or-less the 'JavaScript' way of doing inheritance, and reflects that in it's syntax.

ES6 Class Syntax

class Person {  
  constructor(name) {
    this.name = name;
  }

  sayName() {
    console.log(this.name);
  }
}
var joel = new Person('Joel');  
joel.sayName(); // 'Joel'  

The next version of JavaScript introduces the class keyword and code encapsulation. This also comes with extend, but is a subject for yet-another-post.

Some nice things from this:

There are still some issues with the format: The first being that this is still an issue. Also, it forces the developer to write code that's more single-inheritance oriented. Which isn't a huge issue, but breaks from the power and flexibility that prototypes offer.

There's probably more

These are just a few of the modules patterns I've seen in the wild, and it's easy enough to mix and match them. I'd be curious to know what you're using and why, so sound off. We are on the internet, you know.

Share Comment on Twitter