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:
- explicitly signal which files are needed to make your code function
- reduce the time it takes to resolve value lookup by shortening the lookup chain
- allow minifiers to shorten variable names (they are not able to rename global objects for fear of breaking references elsewhere)
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:
- Model: stores unique data; ignorant of Views; uses Observer pattern to notify components of changes in data (state)
- View: visual representation of Model data; observes a Model for state changes; builds and maintains a DOM element to reflect changes
- Controllers: handles user changes to View and updates Model state accordingly; mediates between Model and View
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:
- Model: stores (and optionally validates) business data; saves/fetches data to/from the server; notifies subscribers of changes
- Collection: ordered set of Models; allows sorting and manipulation of Models; can delegate change notification for nested Models
- Router: routes changes in page url to actions/events; manages browser history (via history API or hash changes)
- View: manages display of DOM element based on changes in Model; can use JavaScript templates to render dynamic markup
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:
- Combine JavaScript and CSS into as few files as possible:
project/vendor$ cat jquery.min.js underscore.min.js > ../www/js/libs.js
- Create sprite sheets for CSS background images
- Inline small CSS background images
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:
- Minimize JavaScript and CSS files with third party tools: uglify.js, closure compiler, YUI compressor
- Optimize image compression with third party tools: pngmini, jpegmini
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:
- Selectors are parsed right to left: avoid overly generic right-hand selectors like
a
,span
,*
, etc - IDs are unique, and therefore unnecessary to nest in selectors
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:
- repaint: when an element changes without affecting page layout (eg.
background-color
) - reflow: when a change to an element causes a page to recalculate layout positions (eg.
float
)
Unfortunately, reflows are common and costly, and are triggered even when some element properties are simply read:
- element:
clientHeight/Width, clientTop/Left, offsetHeight/Width, offsetTop/Left, offsetParent, scrollHeight/Width, scrollTop/Left, focus(), innerText
- image:
width, height
- window:
getComputedStyle(), scrollTo(), scrollX/Y
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';