lesson 6

JS part 3

patterns

The key to writing maintainable code is to apply proven practices to common problems, allowing you to abstract away the low-level details that can often obscure your understanding of how the parts of your code function together. Design Patterns are a broad and varied family of proven solutions to common creational, structural, and behavioural problems. We will cover a few of the more useful patterns here in order to help improve overall code organization and structure.

code patterns

Code patterns in JavaScript are specific to the language, and deal primarily with object creation and strategies for code reuse.

object creation

Because JavaScript doesn't enforce any specific code organization syntax (namespaces, classes, modules, etc), much of the object creation techniques below fall under "best practices":

Namespacing

Namespaces are a common way to avoid naming collisions and excessive global objects by grouping object references in a hierarchical way. JavaScript doesn't provide a native way to namespace object references, but a regular object instance can be used to simulate this behaviour:


    var MyApp = {};
    MyApp.models = {};
    MyApp.models.MainModel = function() { ... };
        

A more generic namespace utility can be used to make namespacing less of a chore:


    function namespace (namespaceString) {
      var parts = namespaceString.split('.'),
        parent = window,
        currentPart = '';
      for (var i = 0, length = parts.length; i < length; i++) {
        currentPart = parts[i];
        parent[currentPart] = parent[currentPart] || {};
        parent = parent[currentPart];
      }
      return parent;
    }

    namespace('MyApp.models.MainModel') = function() { ... };
        
Static Members

Like namespacing, JavaScript has no special syntax for marking properties or methods of an object static (members attached to the Class, not individual instances). Because constructor functions in JavaScript are just another object, it is quite easy to simulate this pattern by attaching members to the constructor function directly:


    function Foo() {}
    Foo.max = 'foo to the max';
    Foo.prototype.min = 'foo to the min';

    var foo = new Foo();
    console.log(foo.max); //undefined
    console.log(foo.min); //'foo to the min'
    console.log(Foo.max); //'foo to the max'
    console.log(Foo.min); //undefined
        
Constants

As with static members, there is no special syntax for constant values in JavaScript, and there is no way to make an object's values immutable. As convention, however, using uppercase names generally signals that those values should not be modified:


    function Foo() {}
    Foo.MAX = 'don`t touch';
        
Dependency Declaration

JavaScript also has no native way to declare dependencies among objects. Nevertheless, it is still good practice to always declare dependencies at the top of your file/module/block. Assigning global or namespaced objects to local variables has the following benefits:


    function Foo() {
      //Dependencies
      var $ = window.jQuery,
        animate = MyLib.utils.animate;

      this.$el = $('#foo');
      ...
    }
        
Chaining

Chaining, a pattern popularized by jQuery, is a simple pattern to include in any object. Just return this for method calls that normally have no return value:


    function Foo() {}
    Foo.walk = function() {
      console.log('walk');
      return this;
    }
    Foo.talk = function() {
      console.log('talk');
      return this;
    }
    var foo = new Foo();
    foo.walk().talk(); //"walk", "talk"
        
Private Members

Like statics, JavaScript has no special syntax for making properties or methods of an object private (only accessible to the object itself). Nevertheless, by leveraging functional scope and closures, we can simulate this behaviour:


    function Foo() {
      var name = 'foo';
      this.getName = function() {
        return name;
      };
    }
        
Module Pattern

The Module Pattern is a formalized structure for packaging code in a self-contained unit. By employing an IIFE, we can easily control the accessibility of an object's members and provide an explicit API for it's use:


    namespace('app.utils.foo') = (function () {
      //Private properties
      var bar = 'bar';
      //Private methods
      var random = function(count) {
        return Math.random() * count
      };

      //API
      return {
        name: 'foo',
        getRandomBars: function(count) {
            return bar + ': ' + random(count);
          }
      };
    })();
        

Taking it one step further, we can use the module pattern to create a more traditional class-based structure by defining an object and only returning its constructor:


    namespace('app.utils.Foo') = (function () {
      //Private properties
      ...
      //Private methods
      ...
      //API
      function Foo(name) {
        this.name = name;
      };
      Foo.prototype.getFancyName = function() {
        return this.name + ' fancy!';
      }
      return Foo; //Return constructor function
    })();

    var foo = new app.utils.Foo('bar');
    foo.getFancyName(); //"bar fancy!"
        

Another common variation is to pass global object references into the IIFE, making our dependency declaration that much easier:


    namespace('app.utils.Foo') = (function ($, win, doc) {
      ...
    })(jQuery, window, document);
        

code reuse

Writing code that is reusable can greatly simplify a program by limiting the total amount of code to manage. We have already discussed mix-ins and borrowing methods from other objects, but it's inheritance, both classical and prototypal, that are often turned to when reusing code among objects. In inheritance patterns, the goal is to describe an object (Parent) that can be easily extended with additional functionality by another object (Child).

Classical Inheritance

Although JavaScript is a prototypical language, and has no strict class syntax, it does have constructors and the new keyword. Some of the other features of classical inheritance can therefore be simulated by manipulating object creation and the prototype chain:


    function inherit(Child, Parent) {
      //copy 'own' properties from parent to child
      for (var key in Parent) {
        if (parent.hasOwnProperty(key)) {
          Child[key] = Parent[key];
        }
      }
      //proxy constructor function
      function Ctor() {
        this.constructor = Child; //set constructor property to point to Child
      }
      //proxy inherits from Parent's prototype (avoid Parent instance)
      Ctor.prototype = Parent.prototype;
      //Child inherits from proxy (requires an object, not function)
      Child.prototype = new Ctor();
      //store reference to Child's 'super'
      Child._super = Parent.prototype;
      //return extended constructor function
      return Child;
    }
        

Using this inherit helper function, we can create objects in a more "classical" way:


    function Parent() {};
    Parent.prototype.talk = function() {
      console.log('hi!');
    }
    function Child() {};
    Child.prototype.walk = function() {
      console.log('walking');
    }
    inherit(Child, Parent);

    var child = new Child();
    child.talk(); //"hi!"
    child.walk(); //"walking"
        
Prototypal Inheritance

Although the goals are the same, Prototypal inheritance avoids newing objects in favour of a more object based approach, thought the mechanics are necessarily similar:


    var parent = {};
    parent.talk = function() {
      console.log('hi!');
    }

    var child = Object.create(parent);
    child.walk = function() {
      console.log('walking');
    }
    child.talk(); //"hi!"
    child.walk(); //"walking"
        

object.create() is a new addition to ES5 (the most recent language spec), though it is relatively simple to polyfill:


    if (typeof Object.create != 'function') {
      Object.create = function(parent, props) {
        //proxy constructor function
        function F() {}
        //proxy inherits from parent
        F.prototype = parent;

        //copy props to proxy
        if (typeof(props) == 'object') {
          for (prop in props) {
            //guard against copying inherited properties
            if (props.hasOwnProperty(prop)) {
              F[prop] = props[prop]
            }
          }
        }
        return new F(); //return instance
      }
    }
        

architectural patterns

Concerned with the behaviour of objects and how they interact and communicate with each other, architectural patterns help organize separate code parts into a working application.

Observer

The Observer pattern describes the relationship between a publisher component and it's subscribers. Subscriber components sign-up with the publisher to be notified of specific changes, and the publisher dispatches updates (with data) to all subscribers when it's state changes. This simple construction has the benefit of loosely coupling components together, as well as promoting flexibility by allowing any number of subscribers to add or remove themselves from notification.

Because the DOM API already uses an eventing system, it is quite natural in JavaScript to use a similar event paradigm for the mechanics of an Observer pattern:


    var publisher = {
      handlers: {},
      on: function(type, callback) {
        if (!(type in this.handlers)) {
          this.handlers[type] = [];
        }
        this.handlers[type].push(callback);
      },
      off: function(type, callback) {
        for (var i = 0, n = this.handlers[type].length; i < n; i++) {
          if (callback === this.handlers[type][i]) {
            this.handlers[type].splice(i, 1);
            break;
          }
        }
      },
      trigger: function(type, data) {
        if (this.handlers && type in this.handlers) {
          var list = this.handlers[type].slice();
          for (var i = 0, n = list.length; i < n; i++) {
            list[i].call(target, data);
          }
        }
      }
    }
        

Using the above, we can easily implement an Observer pattern between two objects:


    var pub = {
      data: null,
      init: function() {
        this.data = 'foo';
        //mix-in publisher behaviour
        extend(this, publisher);
      },
      setData: function(value) {
        this.data = value;
        this.trigger('change:data', this.data);
      }
    };
    var sub = {
      init: function() {
        pub.on('change:data', bind(this.onDataChange, this));
      },
      onDataChange: function(data) {
        console.log('data changed: ', data);
      }
    };
    pub.init();
    sub.init();
    pub.setData('bar'); //"data changed: bar"
        

Mediator

The Mediator pattern allows components to communicate with each other with the help of a central point of control. This pattern prevents too many direct relationships between components, and makes it easier to manage relationships by locating them in one place. This promotes loose coupling by ensuring that components don't directly refer to each other. For example, in an application context, mediators can be used to coordinate "page" state across application components (router), or to coordinate the display state of individual "pages" (display manager).

MVC

The MVC (Model-View-Controller) pattern is a classic and often used application architecture. More of a meta-pattern, combining several common architectural patterns, MVC encourages application organization through a separation of concerns: Models store business data, Views present business data in a user interface, and Controllers manage logic and user input.

In a JavaScript context, the classic MVC pattern has mutated somewhat, and there are now several popular variations that differ in the View-Controller side of things (often called MV*). Classically, the components have the following responsibilities:

Backbone.js

Backbone.js is a popular MVC-like framework that leans quite heavily on the Model side of things, allowing for considerable flexibility when designing application architecture. The primary components of the Backbone.js library include:

Out of the box, Backbone.js is designed to work well with RESTful interfaces, allowing you to easily store and retrieve data to and from the server. With a built-in event system, it is then trivial to wire View updates to Model changes. Backbone.js doesn't have any formal Controllers (though Routers can be considered a very specific kind), with Views often handling user interaction directly, updating their Models when necessary.

A basic game Model:


    var GameModel = Backbone.Model.extend({
      //Default attributes
      defaults: {
        score: 0,
        level: 1,
        userId: null,
        highScore: null
      },
      initialize: function() {
        //Retrieve initial game data from server
        this.fetch({
          success: bind(this.onSuccess, this),
          error: bind(this.onError, this)
        });
      }
      ...
    });
    var game = new GameModel();
        

A basic game View:


    var GameView = Backbone.View.extend({
      //Event handling (automatically bound to this)
      events: {
        "click #btnFire": "onFire",
        "click #btnFly": "onFly"
      },
      render: function() {
        //Render template, inserting model data
        this.$el.html(this.template(this.model.toJSON()));
      },
      onFire: function(evt) {
        evt.preventDefault();
        if (this.enemy.isDead()) {
          //Update Model with new score
          this.model.set('score', this.model.get('score')++);
        }
      }
      ...
    });
    var view = new GameView({
      model: game,
      el: document.getElementById('game')
    });
        

A basic score View:


    var ScoreView = Backbone.View.extend({
      initialize: function() {
        //Register for model score changes (bound to this)
        this.model.on('change:score', render, this);
      },
      render: function() {
        //Update HTML
        this.$el.text(this.model.get('score'));
      }
      ...
    });
    var score = new ScoreView({
      model: game,
      el: document.getElementById('score')
    });
        

modules

Modules (not to be confused with the Module Pattern) are self-contained pieces of code that allow us to formally separate and isolate functionality. Modular code promotes loose coupling and overall code organization, and may even be taken so far as to separate code into different files. Unfortunately, JavaScript currently does not natively support a module loading mechanism (due to be included in future versions), and as a result, several module approaches are available to choose from.

CommonJS

CommonJS modules were designed for use in JavaScript server environments, and provide a simple API for describing and importing modules. At it's most basic, CommonJS modules have access to an exports property that can be decorated with functionality (public API), and a require() method that can be used to access the public API of other modules:


    require.module('my_module', function(exports, require) {
      var property = 'foo';
      var method = function() {
        return 'can't get me';
      };
      exports.property = 'bar';
      exports.method = function() {
        return property + ' got me';
      };
    });

    var module = require('my_module');
    console.log(module.property, module.method()); //"bar", "foo got me"
        
Node.js Modules

Node.js uses a module framework based on CommonJS. Modules are automatically generated from JavaScript files, and given ids based on their file paths (similar to packages in other languages). In addition, a module reference is also passed in, which allows the exports object to be replaced with another value:


    //file: some/path/to/MyModule.js
    function MyModule() {
      this.property = 'foo';
    }
    MyModule.prototype.method = function() {
      return 'bar';
    };
    module.exports = MyModule;
    ...

    //file: server.js
    var MyModule = require('some/path/to/my_module');
    var module = new MyModule();
    console.log(module.property, module.method()); //"foo", "bar"
        

AMD

Although CommonJS modules are well established on the server, their reliance on synchronous module loading (using the local file system) can make them ill suited to use in the browser. The AMD (Asynchronous Module Definition) module framework aims to solve this by allowing modules and their dependencies to be asynchronously loaded. Although the syntax is fairly flexible, the following is a basic example:


    define('myModule', //module id
      ['foo', 'bar'], //dependencies by module id
      function(foo, bar) { //dependencies mapped to local vars
        var myModule = {
          ...
        }
        return myModule; //public API
      }
    );
        

The standard library for AMD modules is require.js, which also includes a tool called r.js used to concatenate files for production.

performance

There are many factors involved in the actual and perceived performance of a site, some of which we have already touched upon.

Network

The network is definitely the slowest link in the server <-> network <-> client chain of responsibility. Optimizing network operations will have a large impact on actual and perceived responsiveness.

Minimize Network Calls

In general, reducing the total number of network calls will minimize overall network latency:

because of the blocking nature of certain asset loading, perceived responsiveness may actually be improved by breaking up assets into smaller packages

Optimize file sizes

Although fewer network calls are generally preferred, smaller file sizes are always better:

Asynchronous Loading

As we discussed earlier, <script> source loading will block the browser until completion. In cases where your application is not directly dependent on a third party library (Google Analytics, Facebook Connect, etc), and execution order is not important, it is possible to trick the browser into asynchronously loading the file:


    (function () {
      window.asyncLoad = function (link) {
        var el = document.createElement('script'),
          firstTag = document.getElementsByTagName('script')[0];
        el.type = 'text/javascript';
        el.async = true;
        el.src = link;
        firstTag.parentNode.insertBefore(el, firstTag);
      };
      window._gaq = [['_setAccount','xxxxxxxx'],
        ['_trackPageview'],
        ['_trackPageLoadTime']];
      window.asyncLoad('//www.google-analytics.com/ga.js');
      window.asyncLoad('//connect.facebook.net/nb_NO/all.js#xfbml=1&appId=xxxxxxxxx');
    })();
        

HTML

As the base document on which CSS and JavaScript operate upon, HTML documents can themselves be a source of performance bottlenecks.

Simplify markup

In all cases, a simple document will render faster than a more complex one. Because an HTML document is converted by the browser into a DOM tree, reducing the complexity of the document will benefit all DOM operations.

Specify image dimensions

Because image loading is non-blocking, explicitly specifying an image's dimensions will help the browser create a correctly sized layout box, and therefore prevent a reflow once the image has loaded.

Load scripts at the bottom

Including <script> tags at the bottom of the body will ensure that styled content is displayed while (blocking) scripts load, improving the perceived responsiveness of the page.

CSS

Because CSS is generally responsible for element layout and display, it can have a large impact on rendering performance.

Optimize selectors

Before any styles can be applied, elements must first be selected. It's advisable to avoid inefficient selector syntax:

Use transitions

By telegraphing intent, CSS transitions will be able to take advantage of native rendering efficiencies that JavaScript animations will never enjoy.

enable hardware acceleration

In some cases, on some platforms (iOS for instance), rendering efficiency may be improved by enabling hardware acceleration:


    .page {
      -webkit-transform: translateZ(0);
      -moz-transform: translateZ(0);
      -o-transform: translateZ(0);
      -ms-transform: translateZ(0);
      transform: translateZ(0);
    }
        

Minimize visual effects

Although they benefit network latency by eliminating the need for images, CSS effects (opacity, box-shadow, text-shadow, etc) can have an adverse impact on rendering efficiency.

JavaScript

Although interacting with the DOM is by far the biggest source of performance issues, JavaScript comes with a couple of it's own notable tricky points.

optimize iterators

When iterating through a large collection of items, small time savings can add up. Never read the list length on each iteration:


    //bad
    for (var i = 0; i < list.length; i++) {
      ...
    }
    //better
    for (var i = 0, n = list.length; i < n; i++) {
      ...
    }
        

use prototype

As previously discussed, taking advantage of an object's prototype is a more memory efficient way to pass on behaviour to decedents.

DOM

The DOM API is certainly the worst performing component of browser-based JavaScript. In most cases, when evaluating the performance impact of DOM operations, the key factor is if and how often the browser is forced to repaint or reflow:

Unfortunately, reflows are common and costly, and are triggered even when some element properties are simply read:

Cache element references

While element selection won't cause a repaint/reflow, DOM tree traversal is also costly. Always cache a reference to a selected element, especially when using jQuery (the jQuery syntax conceals the fact that a selection is being made):


    //bad
    $('#btn').mouseover(onMouseOver);
    $('#btn').mouseout(onMouseOut);

    //better
    var btn = $('#btn');
    btn.mouseover(onMouseOver);
    btn.mouseout(onMouseOut);
        
Work off the tree

As we discussed briefly already, using a document.createDocumentFragment() to build dynamic HTML is more efficient than working directly on the live tree. Other options are to clone or hide the portion of the document you want to modify:


    var clone = el.cloneNode();
    ... //modify
    el.parentNode.replaceChild(clone, el);

    el.style.display = 'none';
    ... //modify
    el.style.display = 'block';
        

school's out...for summer!

60