Building and Maintaining Large JavaScript Applications

Alex Sexton | June 4, 2010

 

To this day, JavaScript is probably most notorious for its amateur or script-kiddie beginnings and often referred to by programmers of other “more established” languages as a hack language, or a toy. These people have no idea what they are talking about. Seriously.

Just because JavaScript is simultaneously more flexible and faster than some of the established languages (ooooh burn…), doesn’t mean that you can’t have an established structure in your JavaScript apps. In fact, it means you get to decide your favorite way of creating that structure. I know, decisions are just opportunities for error, but since when did you become a pessimist?

When I’ve talked about managing large applications in the past, I was criticized for going through many different options and then telling you that you can decide which one you liked the best. I suppose I might be afraid I’d make the wrong choice too, so in this article, I’m going to explain what I would use in my totally sweet large applications, but just know, there are lots of smart people who have different opinions.

I’ll go ahead and assume that you know the following:

  • JavaScript is different than Java
  • Object Literals exist in Javascript
  • My friend Paul doesn’t like cold-cuts (really, he hates them)

JavaScript Inheritance

JavaScript has a native inheritance pattern that is closest to Prototypal Inheritance, though the syntax is a bit more like Classical Inheritance. This weird Frankenstein inheritance pattern was dubbed “Pseudo-classical” by Douglas Crockford.

This isn’t an article on inheritance, so if you need more detail than is given here, I strongly encourage you build a clear understanding of JavaScript’s inheritance pattern prior to building a large application (or operating a motor vehicle).

Just about everything in JavaScript extends from the ‘Object.’ Think of it like “the source” from the movie, The Matrix, if you’d like, but it’s the parent of everything you’ll build. Objects have two places where you can store your properties (properties can be functions and literals and other objects, etc): as an own property, or on the prototype.

Own Properties are not inherited by the children of objects and are generally specific to a single instance. If you were building a person object (we all do that so often), these properties would be things like name, age, and height.  Properties that sit on the prototype would be things like walk, talk, eat; things that are shared among all instances of the object and their children.

The benefit of using the prototype is that there are single common definitions of reusable functions that are available to any object in your application. This is good for memory management, and it’s super DRY (Don’t Repeat Yourself). An added benefit comes from the ability to modify inherited properties on a parent object, and have those modifications reflected in any object that shares those functions in the prototype chain.

// this is called a Constructor Function
var Father = function(firstName, age) {
  this.firstName  = firstName;
  this.age        = age;
};

// the last name should be passed down, so put it on the prototype
Father.prototype.lastName = "Jones";

// use our constructor to create a father
var myFather = new Father("Daddio", 45);

// create a son by running Object.create on the father
var son = Object.create(myFather);

// change the things that are different about the son
son.firstName = "Kiddo";
son.age = 8;

console.log(myFather.firstName+" "+myFather.lastName); // Daddio Jones
console.log(son.firstName + " " + son.lastName); // Kiddo Jones

// Try changing the prototype, aka the last name, the kid will inherit
Father.prototype.lastName = "Smith";

console.log(myFather.firstName+" "+myFather.lastName); // Daddio Smith
console.log(son.firstName + " " + son.lastName); // Kiddo Smith

There are different styles to making inheritance work. They all have advantages and to say the one is concretely better than the other would be naïve. Some of the more popular implementations of the Classical Inheritance pattern in JavaScript, such as MooTools Class, Base2, LowPro, and Simple-Inheritance, make a great companion for those people who were raised with the classical architecture or just need a few more features out of their inheritance pattern.

If I had to tell you to choose one pattern, though, it would be Prototypal Inheritance. It’s been officially added to the language in ECMAScript 5, but you can use the following shim for backwards compatibility.

if (typeof Object.create !== 'function') {     
  Object.create = function (o) 
    function F() {}         
    F.prototype = o;         
    return new F();     
  }; 
}

The beauty of Prototypal Inheritance is in its simplicity.

  1. Create objects by hand.
    var someObject = {
      “type”: “Cool Guy Object”,
      “name” : “Cool Guy 1”};

  2. Clone those objects at some point with Object.create.
    var otherObject = Object.create(someObject);

  3. Fill in the differences later.
    otherObject.name = “Cool Guy 2”;

This, coupled with a system for ‘multiple inheritance’ such as Dojo’s ‘mixin’ or jQuery’s ‘extend,’ can go a long way in keeping your code DRY and clean. You can take two objects and mix their interfaces, in order to get one super-mega-awesome interface.

var superObject = jQuery.extend({}, objectOne, objectTwo);

The idea is not that you have a clear object structure and family lineage, but more importantly that you simply have access to the methods that you want, encapsulated as an object. How you get there depends on your app. Not everything is as straight forward to structure as the ever-popular-totally-realistic Person class you learned in school.

Even more importantly, the whole inheritance part of JavaScript is great, but sometimes its overkill. Either way, I’d suggest you group related methods into object literals and pass those around. You’ll thank yourself later, and it looks way more impressive to your friends and family.

// doing the following is fine, 
// even if you’re afraid of the ‘this’ keyword

var utils = {
  accessDom: function() {
    ...
  },
  doSomething: function() {
    ...
  }
};

// then...

utils.accessDom(‘input’);

THE DOM

If I had to think about the most unmaintainable code I have ever seen, it would certainly be code that was highly reliant on DOM structure. This is especially true for jQuery applications, because they make the DOM so easy to access and manipulate.

There is a simple rule of thumb for interacting with the DOM:

“Expect everything to change.”

If you really keep that in mind when you’re building your application, you can probably skip on to the next part. If that sounds as hard as it is, I’ll give you a few pointers.

Configuration Objects

This is a pretty easy one. Instead of hard-coding the classes and ids that you are interacting with, maintain a configuration object instead. Then, when you need to make selections, reference the configuration object. Don’t forget to have a way to override the object too. This doesn’t just have to apply to ‘selectors’ – in fact, it definitely shouldn’t, but it’s a good place to start, because it’s real easy to pick out.

Here’s a good pattern that most jQuery plugins use, but could be extrapolated to any library, or just straight JavaScript:

jQuery.fn.pluginName = function(options) {
  var defaultOptions = {
    mainContainer : "#main-container",
    buttonClass : ".pluginButton",
    width : "96px"
  };

  // Mixin our options with the defaults. Our options take precedence
  var options = jQuery.extend({}, defaultOptions, options);
};

DOM Traversal

It would be silly to say to ‘never use DOM Traversal.’ However, I would suggest that you take careful consideration when you rely on a DOM structure that you didn’t build. If you don’t have control over a structure in the DOM, you can be sure that it will change on the last day before launch, and it’ll break all your beautiful code.

The alternatives perform a little less impressively, but if you’re willing to sacrifice a few milliseconds for some peace of mind, I’d such using things like jQuery’s ‘closest()’ and ‘children()’ functions, or even better, just name things a little more specifically and select them directly.

In the case of ‘closest()’ and ‘children(),’ you are protected from people adding extra elements into the DOM that would normally break an element.parentNode() solution. These functions will traverse the DOM until a selector is matched.

In most cases, DOM Traversal can be replaced with smarter element linking in your related objects. You can store references to each of the subparts of your element inside your object, instead of just the containing element.

As an example, if you had a video menu with play, pause, and rewind buttons, rather than traversing through “controls” container and attaching events to the first three children elements, you could hold references to each button as you inject the buttons into the dom. Then later when you need to attach events, you can attach them to your guaranteed elements.

If the term “deeply nested” describes your code, you probably have some traversal issues buried deep down from your childhood.

Objects and Dom Elements

This is a little bit more in-depth, but it proves to be incredibly useful in separation of DOM structure and your application. Often times, if you are using good JavaScript Object patterns, you’ll want to directly link an object interface with a specific DOM element, or collection of elements. The most common place you do this is probably with jQuery plugins. Even if you aren’t a huge jQuery user, you probably have seen the concept: “select a bunch of elements, and then do something do them.”

The idea behind a DOM to Object bridge is that often-times, we’ll start with one of those two things, and want quick and easy access to the other. That can be harder said than done.

For instance, if you had an “Alert” object in your code with methods like “display,” “clear,” and “flash,” you’d probably want to link that up to the big red and yellow box that scrolls out of the top of the viewport. If someone clicks on the ‘x’ button in the top right corner, we’ll want to execute the Alert object’s ‘close’ function. That means we started with a DOM element, and we want to call a method on its related object instance.

Conversely, you may also want to be able to programmatically hide the Alert Box DOM element from within the ‘close’ function. You are inside of the Alert object instead of inside of the DOM element, so things like jQuery’s hide() method aren’t going to be immediately useful. This is where a DOM to object bridge comes in.

I’ll build an over-simplified one here to show how you might build a generic one, but I would suggest looking at the best practices for your library and your situation and write one that suits your needs. I also have a blog post about how this can be done easily with jQuery and its plugin architecture. It is more in-depth real life solution if you are looking for a specific example.

var alert = {
  init: function(options, element) {
    this.options = options;
    this.element = element; // ooh that was easy
  },
  close: function() {
    // we can access our single element at any point in our object
    this.element.style.display = "none";
  },
  open: function() {
    this.element.style.display = "block";
  }
};



var bridge = {
  storage: {},
  connect: function(element, obj) {
    this.storage[element.id] = obj;
  },
  getObject: function(element) {
    return this.storage[element.id];
  }
};

// Set it up
var myElement = document.getElementById(‘alertbox’);
alert.init({color: "brown"},);
// connect the element to the object
bridge.connect(myElement, alert);

// Use it

// this function is the click handler for the close button
function XClickHandler(theAlertDOMElement) {
  // since there could be multiple alert objects
  // lets use our bridge to figure out our related object
  var myAlert = bridge.getObject(theAlertDOMElement);

  // now we can use the alert object apis
  myAlert.close();
}

In this example it’s important to note, that we keep DOM manipulation of the element inside the API of its related object. This is the magic. Later on, when your boss comes and asks you to change the close button to a sliding upwards animation, you’ll look like a magician when you can go change the `close` function to use the `slideUp` method in your library, and it works. Automatically. Everywhere. Sure you could do a ‘search and replace’ in your source file, but that gets a lot harder across multiple files and it’s half as cool, at best.

Dependency Management

This is a tricky topic, and it really deserves its own tutorial, but we did inheritance in a few paragraphs and you totally got that, so let’s do this. Dependency management is a necessity if you want to build any application with multiple parts. Rather than go into too much detail on arbitrary facts related to dependency management, I think it’d be better to take a look at two approaches that seem to work best. This is not a non-endorsement for the other available libraries and plugins that do dependency management, but more of a working, and popular solution.

LABjs

LABjs was not even really built with dependency management in mind. It’s more of a performance tool than anything but it fundamentally makes you figure out all of your dependencies ahead of time in order to get that performance boost. So, rather than being a dependency management tool, it’s more of a dependency management enforcement library, with the added speed boost!

With LABjs, you can tell the library what it can load and execute without worrying about other scripts being loaded, as well as tell it what scripts need to wait to execute until after another script.

The code is fairly easy to read:

$LAB
  .load(‘script1.js’)
  .load(‘script2.js’) // scripts 1 and 2 don’t depend on anything
  .wait()
  .load(‘script3.js’) // 3 waits for both script 1 and 2 to complete
  .wait(function(){
    // call functions with 1, 2 or 3
  });

This is the beginnings of a dependency management system, though, you’ll soon find with larger applications, that the loading of arbitrary scripts can cause odd dependency chains, and get a lot more complicated than the example here.

RequireJS

RequireJS is a tool written in JavaScript as an implementation of the CommonJS specifications for dependency management. In other words, it pretty sweet. There are some things that you might not be used to at first, but are pretty easy to get the hang of. I’ll give a few examples:

require([‘app’]);

That’s a pretty place to start. It doesn’t tell us much, but it’s kind of nifty that we can just require our “application module” instead of just running an arbitrary function (yea, I know… we’re still calling the ‘arbitrary’ require function…).

require.def(‘app’, [‘lib/a’, ‘lib/b’], function(a, b) {
  a.init("example");
  b.init("example");
});

Now we’re getting into some of the good stuff. What this says, is that we’re defining the ‘app’ module, and it requires two other modules for it to run. So RequireJS downloads those dependencies and loads instances of those modules into the callback function in the order specified. It follows a folder structure, so based on some base url, ‘lib/a’ would be found at https://www.base-url.com/lib/a.js.

Now, the place where this gets a little tricky, is in defining real modules. The “module pattern” is more or less the act of returning a unique object at the end of a function. This affords a few things like private-ish variables and the ability to return unique instances of objects at the call of a function. This turns out to be a very useful pattern in RequireJS. The modules you define return objects with your given interfaces.

require.def(‘lib/a’, [‘utils/f’], function(f){
  // do something with our f util module
  function privateFunction(name) {
    return f.munge(name);
  }
  
  // return an object that represents ‘a’
  return {
    init: function(name) {
      this.name = "a: " + privatefunction(name);
    }
  };
});

If we assumed this was the content of our ‘lib/a’ module, whenever we run our ‘app’ code, we get an object passed into the callback function that is the return value of our ‘a’ module. It’s important not to mess with variables outside the scope of the module, since multiple instances of the same module would interfere with one another. You can do whatever you’d like inside these functions, but if you intend to load a module, be sure you are returning an object at the end.

RequireJS allows you to separate your code base into a smarter folder structure. It also aids in creating shorter and more task specific files. It forces you to use Objects instead of arbitrary collections of functions defined on the global namespace. These things are essential to being able to maintain code. It’s much easier to target fixes when you know which module is breaking on a specific function. Offering module APIs like the simple one in the example make code easier to test, as well.

RequireJS can theoretically go infinitely deep into your nest of dependencies and it always plays nice when two different modules have the same dependency. It will only load the file once, and the rest of the times, will pull the module from cache to get a new instance at almost no cost. This creates a clean separation of functionality between module instances, even if the same modules are used in multiple places.

End

Anticipate changes. Loosely couple as much as you can. And always stay DRY.

Large applications can be very tough to build the more that temptation catches your attention. If you can resist the urge to pollute your work with poor structural decisions, it will pay out dividends in the end. Writing maintainable JavaScript is possible.

 

About the Author

Alex is a Labs Engineer at Bazaarvoice in Austin, TX. He is a co-host of the yayQuery Podcast and is a founding member of The Society For The Fair Treatment of JavaScript. OK, not really, but he thinks it's a pretty cool language and he wants you to use it right! Apparently, he also talks in third person when writing bios.

Find Alex on: