O'Reilly logo

JavaScript Web Applications by Alex MacCaw

Stay ahead with the world's most comprehensive technology and business learning platform.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, tutorials, and more.

Start Free Trial

No credit card required

Chapter 1. MVC and Classes

Early Days

JavaScript development has changed markedly from how it looked when it was first conceived. It’s easy to forget how far the language has come from its initial implementation in Netscape’s browser, to the powerful engines of today, such as Google’s V8. It’s been a rocky path involving renaming, merging, and the eventual standardization as ECMAScript. The capabilities we have today are beyond the wildest dreams of those early innovators.

Despite its success and popularity, JavaScript is still widely misunderstood. Few people know that it’s a powerful and dynamic object-oriented language. They’re surprised to learn about some of its more advanced features, such as prototypal inheritance, modules, and namespaces. So, why is JavaScript so misunderstood?

Part of the reason is due to previous buggy JavaScript implementations, and part of it is due to the name—the Java prefix suggests it’s somehow related to Java; in reality, it’s a totally different language. However, I think the real reason is the way most developers are introduced to the language. With other languages, such as Python and Ruby, developers usually make a concerted effort to learn the language with the help of books, screencasts, and tutorials. Until recently, though, JavaScript wasn’t given that endorsement. Developers would get requests to add a bit of form validation—maybe a lightbox or a photo gallery—to existing code, often on a tight schedule. They’d use scripts they’d find on the Internet, calling it a day with little understanding of the language behind it. After that basic exposure, some of them might even add JavaScript to their resumes.

Recently, JavaScript engines and browsers have become so powerful that building full-blown applications in JavaScript is not only feasible, but increasingly popular. Applications like Gmail and Google Maps have paved the way to a completely different way of thinking about web applications, and users are clamoring for more. Companies are hiring full-time JavaScript developers. No longer is JavaScript a sublanguage relegated to simple scripts and a bit of form validation—it is now a standalone language in its own right, realizing its full potential.

This influx of popularity means that a lot of new JavaScript applications are being built. Unfortunately, and perhaps due to the language’s history, many of them are constructed very poorly. For some reason, when it comes to JavaScript, acknowledged patterns and best practices fly out the window. Developers ignore architectural models like the Model View Controller (MVC) pattern, instead blending their applications into a messy soup of HTML and JavaScript.

This book won’t teach you much about JavaScript as a language—other books are better suited for that, such as Douglas Crockford’s JavaScript: The Good Parts (O’Reilly). However, this book will show you how to structure and build complex JavaScript applications, allowing you to create incredible web experiences.

Adding Structure

The secret to making large JavaScript applications is to not make large JavaScript applications. Instead, you should decouple your application into a series of fairly independent components. The mistake developers often make is creating applications with a lot of interdependency, with huge linear JavaScript files generating a slew of HTML tags. These sorts of applications are difficult to maintain and extend, so they should be avoided at all costs.

Paying a bit of attention to an application’s structure when you start building it can make a big difference to the end result. Ignore any preconceived notions you have about JavaScript and treat it like the object-oriented language that it is. Use classes, inheritance, objects, and patterns the same way you would if you were building an application in another language, such as Python or Ruby. Architecture is critical to server-side applications, so why shouldn’t the same apply to client-side apps?

The approach this book advocates is the MVC pattern, a tried and tested way of architecting applications that ensures they can be effectively maintained and extended. It’s also a pattern that applies particularly well to JavaScript applications.

What Is MVC?

MVC is a design pattern that breaks an application into three parts: the data (Model), the presentation layer (View), and the user interaction layer (Controller). In other words, the event flow goes like this:

  1. The user interacts with the application.

  2. The controller’s event handlers trigger.

  3. The controller requests data from the model, giving it to the view.

  4. The view presents the data to the user.

Or, to give a real example, Figure 1-1 shows how sending a new chat message would work with Holla.

Holla
Figure 1-1. Sending a new chat message from Holla
  1. The user submits a new chat message.

  2. The controller’s event handlers trigger.

  3. The controller creates a new Chat Model record.

  4. The controller then updates the view.

  5. The user sees his new chat message in chat log.

The MVC architectural pattern can even be implemented without libraries or frameworks. The key is to divide up the responsibilities of the MVC components into clearly defined sections of code, keeping them decoupled. This allows for independent development, testing, and maintenance of each component.

Let’s explore the components of MVC in detail.

The Model

The model is where all the application’s data objects are stored. For example, we might have a User Model that contains a list of users, their attributes, and any logic associated specifically with that model.

A model doesn’t know anything about views or controllers. The only thing a model should contain is data and the logic associated directly with that data. Any event handling code, view templates, or logic not specific to that model should be kept well clear of it. You know an application’s MVC architecture is violated when you start seeing view code in the models. Models should be completely decoupled from the rest of your application.

When controllers fetch data from servers or create new records, they wrap them in model instances. This means that our data is object oriented, and any functions or logic defined on the model can be called directly on the data.

So, rather than this:

var user = users["foo"];
destroyUser(user);

We can do something like this:

var user = User.find("foo");
user.destroy();

The first example is not namespaced or object oriented. If we have another destroyUser() function defined in our application, the two will conflict. Global variables and functions should always be kept to an absolute minimum. In the second example, the destroy() function is namespaced behind User instances, as are all the stored records. This is ideal, since we’re keeping global variables to a minimum, exposing fewer areas to potential conflicts. The code is cleaner and can take advantage of inheritance so functions like destroy() don’t have be defined separately on every model.

Models are explored in much more depth in Chapter 3, which covers topics such as loading in data from servers and creating object-relational mappers (ORMs).

The View

The view layer is what’s presented to the user and is what she interacts with. In a JavaScript application, the view would be made up mostly of HTML, CSS, and JavaScript templates. Apart from simple conditional statements in templates, the views shouldn’t contain any logic.

In fact, like models, views should also be decoupled from the rest of the application. Views shouldn’t know anything about controllers and models—they should be independent. Mixing up views with logic is one of the surest paths to disaster.

That isn’t to say MVC doesn’t allow for presentational logic—as long as it’s not defined inside views. Presentational logic resides in what are called helpers: scripts solely for small utility functions related to the view.

The example below, which includes logic inside views, is something you shouldn’t do:

// template.html
<div>
  <script>
    function formatDate(date) {
      /* ... */
    };
  </script>
  ${ formatDate(this.date) }
</div>

In the code above, we’re inserting the formatDate() function directly into the view, which violates MVC, resulting in an unmaintainable mess of tag soup. By separating out presentational logic into helpers, as with the example below, we’re avoiding that problem and keeping our application’s structure MVC-compliant.

// helper.js
var helper = {};
helper.formatDate = function(){ /* ... */ };

// template.html
<div>
  ${ helper.formatDate(this.date) }
</div>

In addition, all presentational logic is namespaced under the helper variable, preventing conflicts and keeping the code clean and extendable.

Don’t worry too much about specifics regarding views and templates—we cover them extensively in Chapter 5. The aim of this section is to familiarize you with how views relate to the MVC architectural pattern.

The Controller

Controllers are the glue between models and views. Controllers receive events and input from views, process them (perhaps involving models), and update the views accordingly. The controller will add event listeners to views when the page loads, such as those detecting when forms are submitted or buttons are clicked. Then, when the user interacts with your application, the events trigger actions inside the controllers.

You don’t need any special libraries or frameworks to implement controllers; here’s an example using plain old jQuery:

var Controller = {};

// Use a anonymous function to encapsulate scope
(Controller.users = function($){

  var nameClick = function(){
    /* ... */
  };

  // Attach event listeners on page load
  $(function(){
    $("#view .name").click(nameClick);
  });

})(jQuery);

We’re creating a users Controller that is namespaced under the Controller variable. Then, we’re using an anonymous function to encapsulate scope, preventing variable pollution of the global scope. When the page loads, we’re adding a click event listener to a view element.

As you can see, controllers don’t require a library or framework. However, to comply with MVC’s architectural requirements, they must be separated from Models and Views. Controllers and states are covered in more detail in Chapter 4.

Toward Modularity, Creating Classes

Before we get to the nitty-gritty of MVC, we’re going to cover some preliminary concepts, such as JavaScript classes and events. This will give you a solid foundation before moving on to some of the more advanced concepts.

JavaScript object literals are fine for static classes, but it’s often useful to create classical classes with inheritance and instances. It’s important to emphasize that JavaScript is a prototype language, and as such doesn’t include a native class implementation. However, support can be emulated fairly easily.

Classes in JavaScript often get a bad rap, criticized for not being part of the “JavaScript Way,” a term that means essentially nothing. jQuery is effectively neutral when it comes to structural methodology or inheritance patterns. This can lead JavaScript developers to believe they shouldn’t consider structure—i.e., that classes aren’t available or shouldn’t be used. In reality, classes are just another tool, and as a pragmatist, I believe they’re as useful in JavaScript as in any other modern language.

Rather than class definitions, JavaScript has constructor functions and the new operator. A constructor function can specify an object’s initial properties and values when it is instantiated. Any JavaScript function can be used as a constructor. Use the new operator with a constructor function to create a new instance.

The new operator changes a function’s context, as well as the behavior of the return statement. In practice, using new and constructors is fairly similar to languages with native class implementations:

var Person = function(name) {
  this.name = name;
};

// Instantiate Person
var alice = new Person('alice');

// Check instance
assert( alice instanceof Person );

By convention, constructor functions are upper camel-cased to differentiate them from normal functions. This is important because you don’t ever want to call a constructor function without the new prefix.

// Don't do this!
Person('bob'); //=> undefined

The function will just return undefined, and since the context is the window (global) object, you’ve unintentionally created a global variable, name. Always call constructor functions using the new keyword.

When a constructor function is called with the new keyword, the context switches from global (window) to a new and empty context specific to that instance. So, the this keyword refers to the current instance. Although it might sound complicated, in practice, you can treat it like native class implementations in other languages.

By default, if you don’t return anything from a constructor function, this—the current context—will be returned. Otherwise, you can return any nonprimitive type. For example, we could return a function that would set up a new class, the first step in building our own class emulation library:

var Class = function(){
  var klass = function(){
    this.init.apply(this, arguments);
  };
  klass.prototype.init  = function(){};
  return klass;
};

var Person = new Class;

Person.prototype.init = function(){
  // Called on Person instantiation
};

// Usage:
var person = new Person;

Confusingly, due to a JavaScript 2 specification that was never implemented, class is a reserved keyword. The common convention is instead to name class variables as _class or klass.

Adding Functions to Classes

Adding class functions to a constructor function is the same as adding a property onto any object in JavaScript:

Person.find = function(id){ /*...*/ };

var person = Person.find(1);

To add instance functions to a constructor function, you need to use the constructor’s prototype:

Person.prototype.breath = function(){ /*...*/ };

var person = new Person;
person.breath();

A common pattern is to alias a class’ prototype to fn, which is a bit less verbose:

Person.fn = Person.prototype;

Person.fn.run = function(){ /*...*/ };

In fact, you’ll see this pattern throughout jQuery plug-ins, which essentially just add functions to jQuery’s prototype, aliased to jQuery.fn.

Adding Methods to Our Class Library

Currently, our class library includes functionality for instantiating and initializing instances. Adding properties to classes is the same as adding properties to constructor functions.

Properties set directly on the class will be equivalent to static members:

var Person = new Class;

// Static functions are added directly on the class
Person.find = function(id){ /* ... */ };

// And now we can call them directly
var person = Person.find(1);

And properties set on the class’ prototype are also available on instances:

var Person = new Class;

// Instance functions are on the prototype
Person.prototype.save = function(){ /* ... */ };

// And now we can call them on instances
var person = new Person;
person.save();

However, in my opinion, that syntax is a little convoluted, impractical, and repetitive. It’s difficult to see, at a glance, a list of your class’ static and instance properties. Instead, let’s create a different way of adding properties to our classes using two functions, extend() and include():

var Class = function(){
  var klass = function(){
    this.init.apply(this, arguments);
  };

  klass.prototype.init  = function(){};

  // Shortcut to access prototype
  klass.fn = klass.prototype;

  // Shortcut to access class
  klass.fn.parent = klass;

  // Adding class properties
  klass.extend = function(obj){
    var extended = obj.extended;
    for(var i in obj){
      klass[i] = obj[i];
    }
    if (extended) extended(klass)
  };

  // Adding instance properties
  klass.include = function(obj){
    var included = obj.included;
    for(var i in obj){
      klass.fn[i] = obj[i];
    }
    if (included) included(klass)
  };

  return klass;
};

In the improved class library above, we’re adding an extend() function to generated classes, which accepts an object. The object’s properties are iterated through and copied directly onto the class:

var Person = new Class;

Person.extend({
  find:   function(id) { /* ... */ },
  exists: function(id) { /* ... */ }
});

var person = Person.find(1);

The include() function works in exactly the same way, except properties are copied onto the class’ prototype, rather than directly onto the class. In other words, the properties are on the class’ instance, rather than statically on the class.

var Person = new Class;

Person.include({
  save:    function(id) { /* ... */ },
  destroy: function(id) { /* ... */ }
});

var person = new Person;
person.save();

We’re also implementing support for extended and included callbacks. If these properties are present on the passed object, they’ll be invoked:

Person.extend({
  extended: function(klass) {
    console.log(klass, " was extended!");
  }
});

If you’ve used classes in Ruby, this should all look very familiar. The beauty of this approach is that we’ve now got support for modules. Modules are reusable pieces of code, and they can be used as an alternative to inheritance for sharing common properties among classes.

var ORMModule = {
  save: function(){
    // Shared function
  }
};

var Person = new Class;
var Asset  = new Class;

Person.include(ORMModule);
Asset.include(ORMModule);

Class Inheritance Using Prototype

We’ve been using the prototype property a lot, but it hasn’t really been explained yet. Let’s take a closer look at what it is exactly and how to use it to implement a form of inheritance in our classes.

JavaScript is a prototype-based language and—rather than make distinctions between classes and instances—it has the notions of a prototypical object: an object used as a template from which to get the initial properties for a new object. Any object can be associated as a prototype of another object, sharing its properties. In practice, you can look at this as a form of inheritance.

When you fetch a property on an object, JavaScript will search the local object for the property. If it isn’t found, JavaScript will start searching the object’s prototype and continue up the prototype tree, eventually reaching Object.prototype. If the property is found, its value is returned; otherwise, undefined will be returned.

In other words, if you start adding properties to Array.prototype, they’ll be reflected across every JavaScript array.

To subclass a class and inherit its properties, you need to first define a constructor function. Then, you need to assign a new instance of the parent class as the prototype for your constructor function. It looks like this:

var Animal = function(){};

Animal.prototype.breath = function(){
  console.log('breath');
};

var Dog = function(){};

// Dog inherits from Animal
Dog.prototype = new Animal;

Dog.prototype.wag = function(){
  console.log('wag tail');
};

Now, we can check to see whether the inheritance works:

var dog = new Dog;
dog.wag();
dog.breath(); // Inherited property

Adding Inheritance to Our Class Library

Let’s add inheritance to our custom class library. We’ll pass through an optional parent class when creating a new class:

var Class = function(parent){
  var klass = function(){
    this.init.apply(this, arguments);
  };

  // Change klass' prototype
  if (parent) {
    var subclass = function() { };
    subclass.prototype = parent.prototype;
    klass.prototype = new subclass;
  };

  klass.prototype.init = function(){};

  // Shortcuts
  klass.fn = klass.prototype;
  klass.fn.parent = klass;
  klass._super = klass.__proto__;

  /* include/extend code... */

  return klass;
};

If a parent is passed to the Class constructor, we make sure any subclasses share the same prototype. This little dance around creating a temporary anonymous function prevents instances from being created when a class is inherited. The caveat here is that only instance properties, not class properties, are inherited. There isn’t yet a cross-browser way of setting an object’s __proto__;. Libraries like Super.js get around this problem by copying the properties, rather than implementing proper dynamic inheritance.

Now, we can perform simple inheritance by passing parent classes to Class:

var Animal = new Class;

Animal.include({
  breath: function(){
    console.log('breath');
  }
});

var Cat = new Class(Animal)

// Usage
var tommy = new Cat;
tommy.breath();

Function Invocation

Like everything else in JavaScript, functions are just objects. However, unlike other objects, they can be invoked. The context inside the function—i.e., the value of this—depends on where and how it’s invoked.

Apart from using brackets, there are two other ways to invoke a function: apply() and call(). The difference between them has to do with the arguments you want to pass to the function.

The apply() function takes two parameters: a context and an array of arguments. If the context is null, the global context is used. For example:

function.apply(this, [1, 2, 3])

The call() function has exactly the same behavior, yet it is used differently. The first argument is the context, while each subsequent argument is delegated to the invocation. In other words, you use multiple arguments—rather than an array like with apply()—to pass arguments to the function.

function.call(this, 1, 2, 3);

Why would you want to change the context? This is a valid question because other languages get on fine without allowing explicit context changes. JavaScript uses context changes to share state, especially during event callbacks. (Personally, I feel this was a mistake in the design of the language, as it can be confusing for beginners and introduce bugs. However, it’s too late to change it now, so you need to learn how it works.)

jQuery takes advantage of apply() and call() throughout its API to change context—for example, when using event handlers or iterating using each(). This can be confusing at first, but it’s useful when you understand what’s happening:

$('.clicky').click(function(){
  // 'this' refers to the element
  $(this).hide();
});

$('p').each(function(){
  // 'this' refers to the current iteration
  $(this).remove();
});

To access the original context, a common pattern stores the value of this in a local variable. For example:

var clicky = {
  wasClicked: function(){
    /* ... */
  },

  addListeners: function(){
    var self = this;
    $('.clicky').click(function(){
      self.wasClicked()
    });
  }
};

clicky.addListeners();

However, we can use apply to make this much cleaner, wrapping the callback within another anonymous function, which preserves the original context:

var proxy = function(func, thisObject){
  return(function(){
    return func.apply(thisObject, arguments);
  });
};

var clicky = {
  wasClicked: function(){
    /* ... */
  },

  addListeners: function(){
    $('.clicky').click(proxy(this.wasClicked, this));
  }
};

So, in the above example, we specify the context to be used inside the click callback; the context jQuery invokes the function in is ignored. In fact, jQuery’s API includes something to do just this—you guessed it, jQuery.proxy():

$('.clicky').click($.proxy(function(){ /* ... */ }, this));

There are other useful reasons to use apply() and call(), such as delegating. We can delegate calls from one function to another, and even alter the passed arguments:

var App = {
  log: function(){
    if (typeof console == "undefined") return;

    // Turn arguments into a proper array
    var args = jQuery.makeArray(arguments);

    // Insert a new argument
    args.unshift("(App)");

    // Delegate to the console
    console.log.apply(console, args);
  }
};

Above, we’re making an array of arguments and then adding our own. Finally, the call is delegated to console.log(). If you’re not familiar with the arguments variable, it’s set by the interpreter and contains an array of arguments with which the current scope was called. It’s not a true array though—for example, it’s not mutable—so we have to convert it to something usable with jquery.makeArray().

Controlling Scope in Our Class Library

The proxy function described in the previous section is such a useful pattern that we should add it to our class library. We’ll add a proxy function on both classes and instances, allowing us to keep the class’ scope when handing functions off to event handlers and the like:

var Class = function(parent){
  var klass = function(){
    this.init.apply(this, arguments);
  };
  klass.prototype.init = function(){};
  klass.fn = klass.prototype;

  // Adding a proxy function
  klass.proxy = function(func){
    var self = this;
    return(function(){
      return func.apply(self, arguments);
    });
  }

  // Add the function on instances too
  klass.fn.proxy = klass.proxy;

  return klass;
};

We can now use the proxy() function to wrap up functions, making sure they’re invoked in the right scope:

var Button = new Class;

Button.include({
  init: function(element){
    this.element = jQuery(element);

    // Proxy the click function
    this.element.click(this.proxy(this.click));
  },

  click: function(){ /* ... */ }
});

If we didn’t wrap the click() callback with a proxy, it would be called within the context of this.element, rather than Button, causing all sorts of problems. A new specification of JavaScript—ECMAScript, 5th Edition (ES5)—has also added support for controlling invocation scope with the bind() function. bind() is called on a function, making sure the function is called in the context of the specified this value. For example:

Button.include({
  init: function(element){
    this.element = jQuery(element);

    // Bind the click function
    this.element.click(this.click.bind(this));
  },

  click: function(){ /* ... */ }
});

This example is equivalent to our proxy() function, and it makes sure the click() function is called with the correct context. Older browsers don’t support bind() but, luckily, support can be shimmed easily and implemented manually if needed. A shim basically implements a compatibility layer on legacy browsers, directly extending the relevant object’s prototypes, allowing you to use features of ES5 today without worrying about older browsers. For example, a shim that would support bind() would look like this:

if ( !Function.prototype.bind ) {
  Function.prototype.bind = function( obj ) {
    var slice = [].slice,
        args = slice.call(arguments, 1),
        self = this,
        nop = function () {},
        bound = function () {
          return self.apply( this instanceof nop ? this : ( obj || {} ),
                              args.concat( slice.call(arguments) ) );
        };

    nop.prototype = self.prototype;

    bound.prototype = new nop();

    return bound;
  };
}

Function’s prototype is only overwritten if the feature doesn’t already exist: newer browsers will continue to use their native implementations. Shimming is especially useful for arrays, which have had a bunch of new features added in recent JavaScript versions. I personally use the es5-shim project because it covers as many of the new features in ES5 as possible.

Adding Private Functions

So far, any property we’ve added to our classes has been open to the world and can be changed at any time. Let’s now explore how to add private properties to our classes.

A lot of developers end up prefixing private properties with an underscore (_). Although these can still be changed, it makes it obvious that they’re part of a private API. I try to steer clear of this approach because it looks rather ugly.

JavaScript does have support for immutable properties; however, this isn’t implemented across the main browsers, so we’ll have to wait before using this method. Instead, we’ll use JavaScript anonymous functions to create a private scope, which can only be accessed internally:

var Person = function(){};

(function(){

  var findById = function(){ /* ... */ };

  Person.find = function(id){
    if (typeof id == "number")
      return findById(id);
  };

})();

We’re wrapping all our class’ properties in an anonymous function, then creating local variables (findById), which can only be accessed in the current scope. The Person variable is defined in the global scope, so it can be accessed from anywhere.

Never define a variable without using the var operator, since it always creates a global variable. If you need to define a global variable, do so in the global scope or as a property on window:

(function(exports){
  var foo = "bar";

  // Expose variable
  exports.foo = foo;
})(window);

assertEqual(foo, "bar");

Class Libraries

As with a lot of concepts in this book, it’s good to understand the theory behind classes, but often in practice, you’ll use a library. jQuery doesn’t include class support natively, but it can easily be added with a plug-in like HJS. HJS lets you define classes by passing a set of properties to $.Class.create:

var Person = $.Class.create({
  // constructor
  initialize: function(name) {
    this.name = name;
  }
});

To inherit classes, pass their parent as an argument when creating them:

var Student = $.Class.create(Person, {
  pay: function() { /* ... */ }
});

var alex = new Student("Alex");
alex.pay();

To add class properties, set them directly on the class:

Person.find = function(id){ /* ... */ };

HJS’ API also includes a few utility functions, such as clone() and equal():

var alex = new Student("Alex");
var bill = alex.clone();

assert( alex.equal(bill) );

HJS isn’t your only option; Spine also has a class implementation. To use it, just include spine.js in the page:

<script src="http://maccman.github.com/spine/spine.js"> </script>
<script>
  var Person = Spine.Class.create();

  Person.extend({
    find: function() { /* ... */ }
  });

  Person.include({
    init: function(atts){
      this.attributes = atts || {};
    }
  });

  var person = new Person();
</script>

Spine’s class library has a similar API to the library we’ve been building throughout this chapter. Use extend() to add class properties and include() to add instance properties. To inherit from them, pass parent classes to the Spine.Class instantiator.

If you’re widening your gaze beyond jQuery, Prototype is definitely worth checking out. It has an excellent class API that was the inspiration for a lot of other libraries.

jQuery’s John Resig has an interesting post on implementing classical inheritance with the library. It’s well worth reading, especially if you’re interested in the nitty-gritty behind the JavaScript prototype system.

With Safari, you learn the way you learn best. Get unlimited access to videos, live online training, learning paths, books, interactive tutorials, and more.

Start Free Trial

No credit card required