The following document outlines a reasonable style guide for JS development in all codebases. These guidelines strongly encourage the use of existing, common, sensible patterns.
This is a living document and new ideas are always welcome. Please contribute.
The JS Coding Guidelines are divides classes in to several categories:
- Never mix spaces and tabs.
- Use soft tabs with two spaces — they're the only way to guarantee code renders the same in any environment.
-
If using Sublime Text you can do this by hitting
Cmd + ,
. In the tab that opens put the following code between the 2{ }
so it looks like this:{ "tab_size": 2, "translate_tabs_to_spaces": true }
-
(if/else/for/while/try always have spaces, braces and span multiple lines this encourages readability)
BAD
// Examples of really cramped syntax
if(condition) doSomething();
while(condition) iterating++;
for(var i=0;<100;i++) someIterativeFn();
GOOD
// Use whitespace to promote readability
if ( condition ) {
// statements
}
while ( condition ) {
// statements
}
for ( var i = 0; i < 100; i++ ) {
// statements
}
BETTER
var i,
length = 100;
for ( i = 0; i < length; i++ ) {
// statements
}
// Or...
var i = 0,
length = 100;
for ( ; i < length; i++ ) {
// statements
}
var prop;
for ( prop in object ) {
// statements
}
if ( true ) {
// statements
} else {
// statements
}
var foo = 'bar',
num = 1,
undef;
var array = [],
object = {};
Using only one var
per scope (function) promotes readability and keeps your declaration list free of clutter (also saves a few keystrokes)
BAD
var foo = '';
var bar = '';
var qux;
GOOD
var foo = '',
bar = '',
qux;
OR
var // Comment on these
foo = '',
bar = '',
quux;
BAD
function foo() {
// some statements here
var bar = '',
qux;
}
GOOD
function foo() {
var bar = '',
qux;
// all statements after the variables declarations.
}
BAD
function foo() {
let foo,
bar;
if ( condition ) {
bar = '';
// statements
}
}
GOOD
function foo() {
let foo;
if ( condition ) {
let bar = '';
// statements
}
}
function foo( arg1, argN ) {
}
// Usage
foo( arg1, argN );
function square( number ) {
return number * number;
}
// Usage
square( 10 );
function square( number, callback ) {
callback( number * number );
}
// Usage
square( 10, function( square ) {
// callback statements
});
var square = function( number ) {
// Return something valuable and relevant
return number * number;
};
This preferred form has the added value of being able to call itself and have an identity in stack traces:
var factorial = function factorial( number ) {
if ( number < 2 ) {
return 1;
}
return number * factorial( number - 1 );
};
function FooBar( options ) {
this.options = options;
}
// Usage
var fooBar = newFooBar({
a: 'alpha'
});
fooBar.options;
// {
a: 'alpha'
}
foo(function() {
// Note there is no extra space between the first paren
// of the executing function call and the word 'function'
});
// Function accepting an array, no space
foo([
'alpha',
'beta'
]);
foo({
a: 'alpha',
b: 'beta'
});
foo( 'bar' );
if ( !( 'foo' in obj ) ) {
}
The whitespace rules are set forth as a recommendation with a simpler, higher purpose: consistency. It's important to note that formatting preferences, such as 'inner whitespace' should be considered optional, but only one style should exist across the entire source of your project.
if ( condition ) {
// statements
}
while ( condition ) {
// statements
}
for ( var i = 0; i < 100; i++ ) {
// statements
}
if ( true ) {
// statements
} else {
// statements
}
Uuse single quotes unless we are using them in HTML properties.
$('<div class="class"></div>');
(Courtesy jQuery Core Style Guidelines)
typeof variable === 'string'
typeof variable === 'number'
typeof variable === 'boolean'
typeof variable === 'object'
Array.isArray( arrayLikeObject ) (wherever possible)
elem.nodeType === 1
variable === null
variable == null
typeof variable === 'undefined'
variable === undefined
object.prop === undefinedobject.hasOwnProperty( prop )'prop' in object
Consider the implications of the following...
Given this HTML: <input id="foo-input" type="text" value="1">
foo
has been declared with the value 0
and it's type is number
var foo = 0;
// typeof foo;
// 'number'
...
Somewhere later in your code, you need to update foo
with a new value derived from an input element
foo = document.getElementById('foo-input').value;
If you were to test typeof foo
now, the result would be string
This means that if you had logic that tested foo
like:
if ( foo === 1 ) {
importantTask();
}
importantTask()
would never be evaluated, even though foo
has a value of '1'
You can preempt issues by using smart coercion with unary +
or -
operators:
foo =+ document.getElementById('foo-input').value;
// ^ unary `+` operator will convert its right side operand to a number
// typeof foo;
// 'number'
if ( foo === 1 ) {
importantTask();
}
importantTask()
will be called
Here are some common cases along with coercions:
var number = 1,
string = '1',
bool = false;
number;
// 1
number + '';
// '1'
string;
// '1'
+string;
// 1
+string++;
// 1
string;
// 2
bool;
// false
+bool;
// 0
bool + '';
// 'false'
var number = 1,
string = '1',
bool = true;
string === number;
// false
string === number + '';
// true
+string === number;
// true
bool === number;
// false
+bool === number;
// true
bool === string;
// false
bool === !!string;
// true
var array = [ 'a', 'b', 'c' ];
!!~array.indexOf('a');
// true
!!~array.indexOf('b');
// true
!!~array.indexOf('c');
// true
!!~array.indexOf('d');
// false
Note that the above should be considered 'unnecessarily clever' Prefer the obvious approach of comparing the returned value of indexOf, like:
if ( array.indexOf( 'a' ) >= 0 ) {
// ...
}
var num = 2.5;
parseInt( num, 10 );
// is the same as...
~~num;
num >> 0;
num >>> 0;
// All result in 2
// Keep in mind however, that negative numbers will be treated differently...
var neg = -2.5;
parseInt( neg, 10 );
// is the same as...
~~neg;
neg >> 0;
// All result in -2
// However...
neg >>> 0;
// Will result in 4294967294
BAD
if ( array.length > 0 ) ...
GOOD
if ( array.length ) ...
BAD
if ( array.length === 0 ) ...
GOOD
if ( !array.length ) ...
BAD
if ( string !== '' ) ...
GOOD
if ( string ) ...
BAD
if ( string === '' ) ...
GOOD
if ( !string ) ...
BAD
if ( foo === true ) ...
GOOD
if ( foo ) ...
BAD
if ( foo === false ) ...
GOOD
if ( !foo ) ...
...Be careful, this will also match: 0, '', null, undefined, NaN
if ( foo === false ) ...
When only evaluating a ref that might be null
or undefined
, but NOT false, '' or 0
, instead of this:
BAD
if ( foo === null || foo === undefined ) ...
GOOD
if ( foo == null ) ...
Remember, using ==
will match a null
to BOTH null
and undefined
but not false
, '' or 0 null == undefined
ALWAYS evaluate for the best, most accurate result - the above is a guideline, not a dogma.
Prefer ===
over ==
(unless the case requires loose type evaluation)
// === does not coerce type, which means that:
'1' === 1;
// false
// == does coerce type, which means that:
'1' == 1;
// true
true, false
'foo', 1
'', 0, null, undefined, NaN, void, 0
(function( global ) {
var Module = (function() {
var data = 'secret';
return {
// This is some boolean property
bool: true,
// Some string value
string: 'a string',
// An array property
array: [
1,
2,
3,
4
],
// An object property
object: {
lang: 'en-Us'
},
getData:function() {
// get the current value of `data`
return data;
},
setData: function( value ) {
// set the value of `data` and return it
return ( data = value );
}
};
})();
// Other things might happen here
// expose our module to the global object
global.Module = Module;
})( this );
(function( global ) {
function Ctor( foo ) {
this.foo = foo;
return this;
}
Ctor.prototype.getFoo = function() {
return this.foo;
};
Ctor.prototype.setFoo = function( val ) {
return ( this.foo = val );
};
// To call constructor's without `new`, you might do this:
var ctor = function( foo ) {
return newCtor( foo );
};
// expose our constructor to the global object
global.ctor = ctor;
})( this );
const
declarations used as variables are in all caps with _
between each word
const SOMETHING_THAT_IS_CONSTANT = 'I will never change.';
var
and let
declarations use camel case with a lowercase first letter:
let somethingNew = 3;
var anotherNewThing = 8;
Objects are declared using camel case but with the first letter capitalized:
let MilleniumFalcon = new Object();
All modules within will use the Definitive Module Pattern. Within these modules, functions are grouped in to _public
and _private
objects which are const
variables that do not follow the above conventions.
The _private
object stores methods that should not be publicly accessible. The _public
object stores methods that are publicly accessible.
let myDefinitiveModule = (function () {
const _private = {
cacheDom: () => {
this.$something = $( '.something' );
},
bindEvents: () => {
this.$something.on( 'click', function( e ) {
alert( 'You clicked on something' );
});
}
};
const _public = {
init: () => {
_private.cacheDom();
_private.bindEvents();
}
};
return _public;
})();
You are not a human code compiler/compressor, so don't try to be one.
The following code is an example of egregious naming:
BAD
function q( s ) {
return document.querySelectorAll(s);
}
var i,a=[],els=q('#foo');for(i=0;i<els.length;i++){a.push(els[i]);}
GOOD
function query( selector ) {
return document.querySelectorAll( selector );
}
var idx = 0,
elements = [],
matches = query('#foo'),
length = matches.length;
for ( ; idx < length; idx++ ) {
elements.push( matches[ idx ] );
}
'dog' is a string
'dogs' is an array of 'dog' strings
camelCase; function and var declarations
PascalCase; constructor function
rDesc = //;
functionNamesLikeThis;
variableNamesLikeThis;
ConstructorNamesLikeThis;
EnumNamesLikeThis;
methodNamesLikeThis;
SYMBOLIC_CONSTANTS_LIKE_THIS;
Beyond the generally well known use cases of call
and apply
, always prefer .bind( this )
or a functional equivalent, for creating BoundFunction
definitions for later invocation. Only resort to aliasing when no preferable option is available.
function Device( opts ) {
this.value = null;
// open an async stream,
// this will be called continuously
stream.read( opts.path, function( data ) {
// Update this instance's current value
// with the most recent value from the
// data stream
this.value = data;
}.bind(this) );
// Throttle the frequency of events emitted from
// this Device instance
setInterval(function() {
// Emit a throttled event
this.emit('event');
}.bind(this), opts.freq || 100 );
}
// Just pretend we've inherited EventEmitter ;)
When unavailable, functional equivalents to .bind
exist in many modern JavaScript libraries.
// eg. lodash/underscore, _.bind()
function Device( opts ) {
this.value = null;
stream.read( opts.path, _.bind(function( data ) {
this.value = data;
}, this) );
setInterval(_.bind(function() {
this.emit('event');
}, this), opts.freq || 100 );
}
// eg. jQuery.proxy
function Device( opts ) {
this.value = null;
stream.read( opts.path, jQuery.proxy(function( data ) {
this.value = data;
}, this) );
setInterval( jQuery.proxy(function() {
this.emit('event');
}, this), opts.freq || 100 );
}
As a last resort, create an alias to this using self as an Identifier. This is extremely bug prone and should be avoided whenever possible.
function Device( opts ) {
var self = this;
this.value = null;
stream.read( opts.path, function( data ) {
self.value = data;
});
setInterval(function() {
self.emit('event');
}, opts.freq || 100 );
}
Several prototype methods of ES 5.1 built-ins come with a special thisArg
signature, which should be used whenever possible
var obj;
obj = {
f: 'foo',
b: 'bar',
q: 'qux'
};
Object.keys( obj ).forEach(function( key ) {
// |this| now refers to `obj`
console.log( this[ key ] );
}, obj ); // <-- the last arg is `thisArg`
// Prints...
// 'foo'
// 'bar'
// 'qux'
thisArg
can be used with Array.prototype.every
, Array.prototype.forEach
, Array.prototype.some
, Array.prototype.map
, Array.prototype.filter
This section will serve to illustrate ideas and concepts that should not be considered dogma, but instead exists to encourage questioning practices in an attempt to find better ways to do common JavaScript programming tasks.
BAD
function returnLate( foo ) {
var ret;
if ( foo ) {
ret = 'foo';
} else {
ret = 'quux';
}
return ret;
}
GOOD
function returnEarly( foo ) {
if ( foo ) {
return 'foo';
}
return 'quux';
}