JavaScript Style Guide

JavaScript can get messy very quickly. This style guide will help keep our code clean.

Types


Primitives

When you access a primitive type, you work directly on its value.

  • string
  • number
  • boolean
  • null
  • undefined
var foo = 1,
    bar = foo;

bar = 9;
console.log(foo, bar); // => 1, 9

Complex

When you access a complex type, you work on a reference to its value.

  • object
  • array
  • function
var foo = [1, 2],
    bar = foo;

bar[0] = 9;
console.log(foo[0], bar[0]); // => 9, 9

Table of Contents

Objects


Use literal syntax for object creation.

// bad
var item = new Object();

// good var item = {};

Use readable synonyms in place of reserved words.

// bad
var superman = {
  class: 'alien'
};

// bad var superman = { klass: 'alien' };
// good var superman = { type: 'alien' };

Table of Contents

Arrays


Use the literal syntax for array creation.

// bad
var items = new Array();

// good var items = [];

If you don't know array length, use Array.push().

var someStack = [];

// bad someStack[someStack.length] = 'abracadabra';
// good someStack.push('abracadabra');

When you need to copy an array, use Array.slice().

var len = items.length,
    itemsCopy = [],
    i;

// bad for (i = 0; i < len; i+=1) { itemsCopy[i] = items[i]; }
// good itemsCopy = items.slice();

To convert an array-like object to an array, use Array.slice().

var args = Array.prototype.slice.call(arguments);

Table of Contents

Strings


Use single quotes ' ' for strings.

// bad
var name = "Zach Forrest";

// good var name = 'Zach Forrest';
// bad var fullName = "Zach " + this.lastName;
// good var fullName = 'Zach ' + this.lastName;

Table of Contents

Functions


Function expressions:

// anonymous function expression
var anonymous = function() {
  return true;
};

// named function expression var named = function named() { return true; };
// immediately-invoked function expression (IIFE) (function() { console.log('Welcome to the Internet. Please follow me.'); })();

Never declare a function in a non-function block (if, while, etc). Assign the function to a variable instead. Browsers will allow you to do it, but they all interpret it differently, which is bad news bears.

// bad
if (currentUser) {
  function test() {
    console.log('Nope.');
  }
}

// good var test; if (currentUser) { test = function test() { console.log('Yup.'); }; }

Never name a parameter arguments, this will take precedence over the arguments object that is given to every function scope.

// bad
function nope(name, options, arguments) {}

// good function yup(name, options, args) {}

Table of Contents

Properties


Use dot notation when accessing properties.

var luke = {
  jedi: true,
  age: 28
};

// bad var isJedi = luke['jedi'];
// good var isJedi = luke.jedi;

Use subscript notation [ ] when accessing properties with a variable.

var luke = {
    jedi: true,
    age: 28
};

function getProp(prop) { return luke[prop]; }
var isJedi = getProp('jedi');

Table of Contents

Variables


Always use var to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace. Captain Planet warned us of that.

// bad
superPower = new SuperPower();

// good var superPower = new SuperPower();

Use one var declaration for multiple variables and declare each variable on a new line.

// bad
var items = getItems();
var goSportsTeam = true;
var dragonball = 'z';

// good var items = getItems(), goSportsTeam = true, dragonball = 'z';

Declare unassigned variables last. This is helpful when later on you might need to assign a variable depending on one of the previous assigned variables.

// bad
var i, len, dragonball,
    items = getItems(),
    goSportsTeam = true;

// bad var i, items = getItems(), dragonball, goSportsTeam = true, len;
// good var items = getItems(), goSportsTeam = true, dragonball, length, i;

Assign variables at the top of their scope. This helps avoid issues with variable declaration and assignment hoisting related issues.

// bad
function() {
  if (!arguments.length) {
    return false;
  }

var name = getName();
return true; }
// good function() { var name = getName();
if (!arguments.length) { return false; }
return true; }

Table of Contents

Hoisting


Variable declarations get hoisted to the top of their scope, their assignment does not.

// we know this wouldn't work (assuming there
// is no notDefined global variable)
function example() {
  console.log(notDefined); // => throws a ReferenceError
}

// creating a variable declaration after you // reference the variable will work due to // variable hoisting. Note: the assignment // value of `true` is not hoisted. function example() { console.log(declaredButNotAssigned); // => undefined var declaredButNotAssigned = true; }
// The interpreter is hoisting the variable // declaration to the top of the scope. // Which means our example could be rewritten as: function example() { var declaredButNotAssigned; console.log(declaredButNotAssigned); // => undefined declaredButNotAssigned = true; }

Anonymous function expressions hoist their variable name, but not the function assignment.

function example() {
  console.log(anonymous); // => undefined

anonymous(); // => TypeError anonymous is not a function
var anonymous = function() { console.log('anonymous function expression'); }; }

Named function expressions hoist the variable name, not the function name or the function body.

function example() {
  console.log(named); // => undefined

named(); // => TypeError named is not a function
superPower(); // => ReferenceError superPower is not defined
var named = function superPower() { console.log('Flying'); }; }
// the same is true when the function name // is the same as the variable name. function example() { console.log(named); // => undefined
named(); // => TypeError named is not a function
var named = function named() { console.log('named'); } }

Function declarations hoist their name and the function body.

function example() {
  superPower(); // => Flying

function superPower() { console.log('Flying'); } }

Table of Contents

Conditional Expressions & Equality


Use === and !== over == and !=.

Conditional expressions are evaluated using coercion with the ToBoolean method and always follow these simple rules:

  • Objects evaluate to true
  • Undefined evaluates to false
  • Null evaluates to false
  • Booleans evaluate to the value of the boolean
  • Numbers evalute to false if +0, -0, or NaN, otherwise true
  • Strings evaluate to false if an empty string ' ', otherwise true
if ([0]) {
  // true
  // An array is an object, objects evaluate to true
}

Use shortcuts.

// bad
if (name !== '') {
  // ...stuff...
}

// good if (name) { // ...stuff... }
// bad if (collection.length > 0) { // ...stuff... }
// good if (collection.length) { // ...stuff... }

Table of Contents

Blocks


Use braces with all multi-line blocks.

// bad
if (test)
  return false;

// good if (test) return false;
// good if (test) { return false; }
// bad function() { return false; }
// good function() { return false; }

Table of Contents

Comments


Use // for comments. Place single line comments on a newline above the subject of the comment. Put an empty line before the comment.

// bad
var active = true;  // is current tab

// good // is current tab var active = true;
// bad function getType() { console.log('fetching type...'); // set the default type to 'no type' var type = this._type || 'no type';
return type; }
// good function getType() { console.log('fetching type...');
// set the default type to 'no type' var type = this._type || 'no type';
return type; }

Prefixing your comments with FIXME or TODO helps other developers quickly understand if you're pointing out a problem that needs to be revisited, or if you're suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions are FIXME -- need to figure this out or TODO -- need to implement.

Use // FIXME: to annotate problems.

function Calculator() {

// FIXME: shouldn't use a global here total = 0;
return this; }

Use // TODO: to annotate solutions to problems.

function Calculator() {

// TODO: total should be configurable by an options param this.total = 0;
return this; }

Table of Contents

Whitespace


Use soft tabs set to 2 spaces.

// good
function() {
∙∙var name;
}

// bad function() { ∙var name; }
// bad function() { ∙∙∙∙var name; }

Place 1 space before the leading brace.

// bad
function test(){
  console.log('test');
}

// good function test() { console.log('test'); }
// bad dog.set('attr',{ age: '1 year', breed: 'Bernese Mountain Dog' });
// good dog.set('attr', { age: '1 year', breed: 'Bernese Mountain Dog' });

Use indentation when making long method chains.

// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();

// good $('#items') .find('.selected') .highlight() .end() .find('.open') .updateCount();

Table of Contents

Commas


Leading commas: Nope.

// bad
var once
  , upon
  , aTime;

// good var once, upon, aTime;

Additional trailing comma: Nope.

// bad
var hero = {
  firstName: 'Kevin',
  lastName: 'Flynn',
};

var heroes = [ 'Batman', 'Superman', ];
// good var hero = { firstName: 'Kevin', lastName: 'Flynn' };
var heroes = [ 'Batman', 'Superman' ];

Table of Contents

Semicolons


Yup.

// bad
(function() {
  var name = 'Skywalker'
  return name
})()

// good (function() { var name = 'Skywalker'; return name; })();
// good ;(function() { var name = 'Skywalker'; return name; })();

Table of Contents

Type Casting & Coercion


Perform type coercion at the beginning of the statement.

//  => this.reviewScore = 9;

// bad var totalScore = this.reviewScore + '';
// good var totalScore = '' + this.reviewScore;
// bad var totalScore = '' + this.reviewScore + ' total score';
// good var totalScore = this.reviewScore + ' total score';

Use parseInt for Numbers and always with a radix for type casting.

var inputValue = '4';

// bad var val = new Number(inputValue);
// bad var val = +inputValue;
// bad var val = inputValue >> 0;
// bad var val = parseInt(inputValue);
// good var val = Number(inputValue);
// good var val = parseInt(inputValue, 10);

Booleans

var age = 0;

// bad var hasAge = new Boolean(age);
// good var hasAge = Boolean(age);
// good var hasAge = !!age;

Table of Contents

Naming Conventions


Avoid single letter names. Be descriptive with your naming.

// bad
function q() {
  // ...stuff...
}

// good function query() { // ..stuff.. }

Use camelCase when naming objects, functions, and instances.

// bad
var OBJEcttsssss = {};
var this_is_my_object = {};
function c() {};
var u = new user({
  name: 'Zachary Forrest'
});

// good var thisIsMyObject = {}; function thisIsMyFunction() {}; var user = new User({ name: 'Zachary Forrest' });

Use PascalCase when naming constructors or classes.

// bad
function user(options) {
  this.name = options.name;
}

var bad = new user({ name: 'nope' });
// good function User(options) { this.name = options.name; }
var good = new User({ name: 'yup' });

When saving a reference to this use self.

function() {
  var self = this;
  return function() {
    console.log(self);
  };
}

Name your functions. This is helpful for stack traces.

// bad
var log = function(msg) {
  console.log(msg);
};

// good var log = function log(msg) { console.log(msg); };

Table of Contents

jQuery


Prefix jQuery object variables with a $.

// bad
var sidebar = $('.sidebar');

// good var $sidebar = $('.sidebar');

Cache jQuery lookups.

// bad
function setSidebar() {
  $('.sidebar').hide();

// ...stuff... $('.sidebar').css({ 'background-color': 'pink' }); }
// good function setSidebar() { var $sidebar = $('.sidebar'); $sidebar.hide();
// ...stuff...
$sidebar.css({ 'background-color': 'pink' }); }

For DOM queries use Cascading $('.sidebar ul') or parent > child $('.sidebar > ul').

Use find with scoped jQuery object queries.

// bad
$('ul', '.sidebar').hide();

// bad $('.sidebar').find('ul').hide();
// good $('.sidebar ul').hide();
// good $('.sidebar > ul').hide();
// good $sidebar.find('ul');

Table of Contents

Unit Testing


Unit testing your JavaScript is strongly encouraged. Suit has the Karma testing framework built right in, so your tests can be written with Jasmine. When contributing any JavaScript to Suit, unit tests are required.


Table of Contents

jQuery Plugin Template


When writing a jQuery plugin, using the following template will make your life easier.

// [myPlugin] Plugin
// Plugin description.
// Author: null
// Last Updated: mm/dd/yyyy
// Unit Tests: [true|false]

// Do not actually name your plugin myPlugin
// jslint browser: true, eqeq: true, es5: false, regexp: true, sloppy: true, unparam: true, white: true, indent: 4, maxerr: 100 // global jQuery: true, Modernizr: true
(function($) {
'use strict';
$.tmpl = $.tmpl || function(a,b){return(String(a)).replace(/\{\{(= )?([^\{\}]+)\}\}/g, function(c,d,e){return (b||{}).hasOwnProperty(e)?(/^f/.test(typeof b[e])?b[e]():b[e]):c;});};
$.fn.myPlugin = function(method) {
var plugin = { name: 'myPlugin', version: '1.0.0' }, methods, helpers;
helpers = {
privateFoo: function() { // I am a function, please add some code so I can be useful }
};
methods = {
init: function(options) { var $opts = $.extend({}, $.fn.myPlugin.defaults, options); return this.each(function() { var $self = $(this).data(plugin.name, $opts); // I am a function, please add some code so I can be useful }); },
publicFoo: function() { return this.each(function() { var $self = $(this), $opts = $self.data(plugin.name); if ($opts) { // the plugin has been initialized for this element, do something } }); }
};
// No need to change anything below here if (typeof methods[method] === 'function') { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); }
if (typeof method === 'object' || !method) { return methods.init.apply(this, arguments); }
return $.error($.tmpl('Method {{= method}} does not exist in plugin {{= name}} {{= version}}', $.extend({method: method}, plugin)).text());
};
$.fn.myPlugin.defaults = { foo: 'bar' };
}(jQuery));

Table of Contents