Javascript style guide
Switch branches/tags
Nothing to show
Clone or download
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.eslintrc
LICENSE
README.md

README.md

Javascript style guide

The Javascript style guide used at Procurios is inspired by and laid on top of the Airbnb style guide. We ended up creating our own (forking) because:

  1. We aim for synergy between our PHP and JS standards.
  2. Many existing style guides are too restrictive for our taste.

Our style guides makes a distinction between rules and recommendation. A rule must be followed, but it is allowed to break with recommendations if there's a good reason to do so.

Table of Contents

Rules

Strings

Quotes

Use single quotes ('') for strings. Double quotes are allowed if the string contains a quote that would have to be escaped.

// bad
var name = "Bob Parr";

// good
var name = 'Bob Parr';

// bad
var fullName = "Bob " + this.lastName;

// good
var fullName = 'Bob ' + this.lastName;

// bad
var greeting = "Hi Bob! How is the weather today?";

// good
var greeting = "Hi Bob! How's the weather today?";

↑ back to top

Functions

Declaration

We use one of the following ways to declare a function:

// 1. A function defined with the Function constructor assigned to the variable multiply
function multiply (x, y) {
	return x * y;
}

// 2. A function expression of a function named func_name assigned to the variable multiply
var multiply = function funcName (x, y) {
	return x * y;
};

Both do approximately the same thing, with a few subtle differences. Instead of forcing a way to declare a function, inform yourself so you know what happens when you pick a one (read more).

There's an exception for immediately-invoked function expressions (IIFE):

;(function () {
	console.log('Welcome to the Internet. Please follow me.');
})();

An IIFE starts with a semicolon to prevent errors when concatenating files.

↑ back to top

Always name functions

Naming functions:

  • makes code easier to read.
  • gets you stacktraces that reference actual function names.

For example:

// bad
function (x, y) {
	return x * y;
}

// bad
var multiply = function (x, y) {
	return x * y;
}

// good
function multiply (x, y) {
	return x * y;
}

// good
var multiply = function multiply (x, y) {
	return x * y;
}

↑ back to top

Never declare a function in a non-function block

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
if (currentUser) {
	test = function test () {
		console.log('Yup.');
	};
}

↑ back to top

Variables

Always use var

Always use var to declare variables. Not doing so will result in global variables. We want to avoid polluting the global namespace.

// bad
car = new Car();

// good
var car = new Car();

One var declaration per variable

Use one var declaration per variable. It's easier to add new variable declarations this way, and you never have to worry about swapping out a ; for a , or introducing punctuation-only diffs.

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

// bad (compare to above, and try to spot the mistake)
var items = getItems(),
	goSportsTeam = true;
	dragonball = 'z';

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

↑ back to top

Comparison Operators & Equality

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

// bad
if (a == b) {
	// do stuff
}

// good
if (a === b) {
	// do stuff
}

For more information see Truth Equality and JavaScript by Angus Croll.

↑ back to top

Blocks

Use braces with all multi-line blocks

// bad
if (test)
	// do something

// bad
if (test) // do something

// good
if (test) {
	// do something
}

// bad
function () { // do something }

// good
function () {
	// do something
}

↑ back to top

Put else on the same line as your if block's closing brace

// bad
if (test) {
	thing1();
	thing2();
}
else {
	thing3();
}

// good
if (test) {
	thing1();
	thing2();
} else {
	thing3();
}

↑ back to top

Comments

Use /** ... */ for multi-line comments

Include a description, specify types and values for all parameters and return values using JSDoc.

// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make (tag) {
	// ...stuff...
	return element;
}

// good
/**
 * make() returns a new element
 * based on the passed in tag name
 *
 * @param {String} tag
 * @return {Element} element
 */
function make (tag) {
	// ...stuff...
	return element;
}

↑ back to top

Use // for single line comments

// 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;
}

↑ back to top

Whitespace

Use tabs to indent your code

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

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

// good
function () {
	var name;
}

↑ back to top

Place 1 space before the leading brace.

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

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

↑ back to top

Place 1 space before opening parenthesis

// bad
if(isJedi) {
  fight ();
}

// good
if (isJedi) {
  fight();
}

// bad
function fight() {
  console.log ('Swooosh!');
}

// good
function fight () {
  console.log('Swooosh!');
}

↑ back to top

Set off operators with spaces

// bad
var x=y+5;

// good
var x = y + 5;

↑ back to top

Commas

Don't use leading commas

// bad
var story = [
	once
	,upon
	,aTime
];

// good
var story = [
	once,
	upon,
	aTime
];

// bad
var hero = {
	firstName: 'Bob'
	,lastName: 'Parr'
	,heroName: 'Mr. Incredible'
	,superPower: 'strength'
};

// good
var hero = {
	firstName: 'Bob',
	lastName: 'Parr',
	heroName: 'Mr. Incredible',
	superPower: 'strength'
};

↑ back to top

Don't place an additional trailing comma

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

var heroes = [
	'Batman',
	'Superman',
];

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

var heroes = [
	'Batman',
	'Superman'
];

This can cause problems with IE6/7 and IE9 if it's in quirksmode. Also, in some implementations of ES3 would add length to an array if it had an additional trailing comma. This was clarified in ES5 (source):

Edition 5 clarifies the fact that a trailing comma at the end of an ArrayInitialiser does not add to the length of the array. This is not a semantic change from Edition 3 but some implementations may have previously misinterpreted this.

↑ back to top

Semicolons

Never leave out semicolons

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

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

// good (guards against the function becoming an argument when two files with IIFEs are concatenated)
;(function() {
	var name = 'Skywalker';
	return name;
})();

Read more about guarding IIFEs.

↑ back to top

Naming

Use camelCase when naming variables, objects and functions

// bad
var OBJEcttsssss = {};
var this_is_my_object = {};
var o = {};
function c () {}

// good
var thisIsMyObject = {};
function thisIsMyFunction () {}

↑ back to top

Use PascalCase when naming constructors or classes

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

var user = new user({
	name: 'nope'
});

// good
function User (options) {
	this.name = options.name;
}

var user = new User({
	name: 'yup'
});

// bad
function OrderLine (options) {
	this.amount = options.amount;
}

var OrderLine = new OrderLine({
	amount: 100
});

// good
function OrderLine (options) {
	this.amount = options.amount;
}

var orderLine = new OrderLine({
	amount: 100
});

↑ back to top

When saving a reference to this use _this

// bad
function () {
	var self = this;
}

// bad
function () {
  var that = this;
}

// good
function () {
  var _this = this;
}

↑ back to top

Modules (requirejs)

  • The file should be named with PascalCase, and match the name of the single export.
  • Always declare 'use strict'; at the top of the module.
  • Wrap your module in a IIFE if it contains dynamic dependencies.

Example:

;(function () {
	'use strict';

	var dependencies = [];

	if (!('classList' in document.createElement('p'))) {
		dependencies.push('path/to/classList.min.js');
	}

	define(dependencies,
		/**
		 * @returns {ExampleModule}
		 */
		function () {
			var ExampleModule = function () {
				// stuff
			}

			return ExampleModule;
		}
	);
})();

↑ back to top

Recommendations

Objects

Creation

Use the literal syntax for object creation.

// bad
var item = new Object();

// good
var item = {};

{} has the same return value as new Object(), but has the following advantages:

  • new Object() can be shadowed (and might have a different return value than expected)
  • {} is shorter (less typing, KISS)
  • {} can be partially optimized at parse time

↑ back to top

Arrays

Creation

Use the literal syntax for array creation.

// bad
var items = new Array();

// good
var items = [];

Using [] has the following advantages:

  • It's much faster.
  • It can't be shadowed (always has the same return value).
  • Less typing.

↑ back to top

Copying

When you need to copy an array use Array#slice. It's fast.

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

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

// good
itemsCopy = items.slice();

↑ back to top

Converting

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

function trigger () {
	var args = Array.prototype.slice.call(arguments);
	...
}

↑ back to top

Functions

Don't 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) {
	// ...stuff...
}

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

↑ back to top

Variables

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;
var items = getItems();
var dragonball;
var goSportsTeam = true;
var len;

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

↑ back to top

Assign variables at the top of their scope

This helps avoid issues with variable declaration and assignment hoisting related issues.

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

	//..other stuff..

	var name = getName();

	if (name === 'test') {
		return false;
	}

	return name;
}

// good
function () {
	var name = getName();

	test();
	console.log('doing stuff..');

	//..other stuff..

	if (name === 'test') {
		return false;
	}

	return name;
}

// bad - unnecessary function call
function () {
	var name = getName();

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

	this.setFirstName(name);

	return true;
}

// good
function() {
	var name;

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

	name = getName();
	this.setFirstName(name);

	return true;
}

↑ back to top

Comparison Operators & Equality

Use shortcuts

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

// good
if (name) {
	// ...stuff...
}

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

// good
if (collection.length) {
	// ...stuff...
}

↑ back to top

Whitespace

Use indentation when making long method chains

Use a leading dot, which emphasizes that the line is a method call, not a new statement.

// bad
var DraftOrderLine = (OrderLineApi.getOrderLine()).setPrice(1.00).setAmount(1200).setProductId(1).setDescription('Foobar');

// good
var DraftOrderLine = (OrderLineApi.getOrderLine())
	.setPrice(1.00)
	.setAmount(1200)
	.setProductId(1)
	.setDescription('Foobar');

↑ back to top

Type Casting & Coercion

Perform type coercion at the beginning of the statement.

// bad
var totalScore = this.reviewScore + '';

// good
var totalScore = '' + this.reviewScore;

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

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

↑ back to top

Be careful when using bitshift operations

Numbers are represented as 64-bit values, but Bitshift operations always return a 32-bit integer (source). Bitshift can lead to unexpected behavior for integer values larger than 32 bits. Discussion. Largest signed 32-bit Int is 2,147,483,647:

2147483647 >> 0 //=> 2147483647
2147483648 >> 0 //=> -2147483648
2147483649 >> 0 //=> -2147483647

↑ back to top

Naming

Be descriptive with your naming

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

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

↑ back to top

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);
};

↑ back to top

Constructors

Assign methods to the prototype object

... instead of overwriting the prototype with a new object. Overwriting the prototype makes inheritance impossible: by resetting the prototype you'll overwrite the base!

function Jedi () {
	console.log('new jedi');
}

// bad
Jedi.prototype = {
	fight: function fight () {
		console.log('fighting');
	},
	block: function block () {
		console.log('blocking');
	}
};

// good
Jedi.prototype.fight = function fight () {
	console.log('fighting');
};

Jedi.prototype.block = function block () {
	console.log('blocking');
};

↑ back to top

return this to help with method chaining

// bad
Jedi.prototype.jump = function () {
	this.jumping = true;
    return true;
};

Jedi.prototype.setHeight = function (height) {
	this.height = height;
};

var Luke = new Jedi();
Luke.jump(); // => true
Luke.setHeight(20); // => undefined

// good
Jedi.prototype.jump = function () {
	this.jumping = true;
	return this;
};

Jedi.prototype.setHeight = function (height) {
	this.height = height;
	return this;
};

var Luke = new Jedi();

Luke.jump()
	.setHeight(20);

↑ back to top