Saiku JavaScript code styleguide.
- Semicolons
- Strict mode
- Variables
- Prototypes
- Regular Expressions
- Objects
- Properties
- Arrays
- Strings
- Numbers
- Functions
- Equality comparisons
- Conditionals
- Blocks
- Console Statements
- Comments
- Naming conventions
- Whitespace
- Type checking
- jQuery
- Code linting
- Resources
- 1.1 Always use semicolons.
ASI can be tricky, don't rely on it.
- 1.2 Don't start function blocks nor anonymous self-invoking functions with semicolons.
// Bad
;(function() {
console.log('I\'m too cool for school');
})();
// Good
(function() {
console.log('I\'m awesome');
})();
- 2.1 Make use of the strict mode pragma.
Just be aware not to use it globally.
// Bad (pragma is being used globally)
'use strict';
function doSomething() {
}
function doSomethingElse() {
}
// Good
function doSomething() {
'use strict';
// stuff
}
// Good
(function() {
'use strict';
function doSomething() {
}
function doSomethingElse() {
}
})();
- 3.1 Always use
var
to declare variables.
Not doing so will result in global variables. We want to avoid polluting the global namespace.
// Bad
superPower = new SuperPower();
// Good
var superPower = new SuperPower();
- 3.2 Use one
var
declaration per variable.
This allows for better debuggability and avoid some annoying problems.
// Bad
var foo = 1,
bar = 2,
baz = 3;
// Good
var foo = 1;
var bar = 2;
var baz = 3;
- 3.3 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 foo = 1;
var bar;
var baz;
var number = 42;
// Good
var foo = 1;
var number = 42;
var bar;
var baz;
- 3.4 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...');
// ...
var name = getName();
if (name === 'test') {
return false;
}
return name;
}
// Good
function() {
var name = getName();
test();
console.log('Doing 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;
}
- 3.5 Use
||
to define default values.
// Bad
function setNumber(value) {
if (!value) {
value = 42;
}
return value;
}
// Good
function setNumber(value) {
return value || 42;
}
- 4.1 Hacking native prototypes should be avoided at all costs, use a method instead.
// Bad
String.prototype.half = function () {
return this.substr(0, this.length / 2);
};
// Good
function half(text) {
return text.substr(0, text.length / 2);
}
- 4.2 Don't instantiate an object unless you assign it to a variable and use it.
Not doing so configures a code smell and means that the constructor should be replaced with a function that doesn't require
new
to be used.
// Bad
new Person();
// Bad (`person` is never used)
var person = new Person();
// Good
Person();
// Good
Person.create();
- 5.1 Keep regular expressions in variables, don't use them inline.
This will vastly improve readability.
// Bad
if (/\d+/.test(text)) {
console.log('So many numbers!');
}
// Good
var matchNumbers = /\d+/;
if (matchNumbers.test(text)) {
console.log('So many numbers!');
}
- 6.1 Use the literal syntax for object creation.
// Bad
var foo = new Object();
// Good
var bar = {};
- 6.2 Do not use reserved words as object keys.
They can cause problems in older versions of IE if not wrapped within quotes.
// Bad
var foo = {
class: 'Foo',
default: true
};
// Good
var baz = {
type: 'Baz',
isDefault: true
};
- 6.3 Use
camelCase
when naming object keys.
This way is easier to use dot notation to access object values.
// Bad
var user = {
'name': 'John',
'has-children': false,
'is-underage': false,
'age': 42
};
// Good
var user = {
name: 'John',
hasChildren: false,
isUnderage: false,
age: 42
};
- 7.1 Use dot notation to access properties.
Dot notation is simpler and enforces
camelCase
when naming object keys.
var user = {
name: 'John',
age: 42
};
// Bad
var userName = user['name'];
// Good
var userAge = user.age;
- 8.1 Use the literal syntax for array creation.
// Bad
var foo = new Array();
// Good
var bar = [1, 2, 3];
- 8.2 Use
Array#push
instead of direct assignment to add items to an array.
var groceries = [];
// Bad
groceries[groceries.length] = 'tomato';
// Good
groceries.push('oreo');
- 8.3 To cleanup an array set its length to zero.
// Bad
var foo = [1, 3, 5];
foo = null;
// Good
var bar = [2, 4, 6];
bar.length = 0;
- 8.4 Use
Array#slice
to clone an array.
var total = items.length;
var itemsCopy = [];
// Bad
for (var i = 0; i < total; i++) {
itemsCopy[i] = items[i];
}
// Good
itemsCopy = items.slice();
- 8.5 To convert an array-like object to an array, use
Array#slice
.
function argsToArray() {
return Array.prototype.slice.call(arguments);
}
- 9.1 Use single quotes
'
for strings.
// Bad
var reject = "My cat";
// Good
var god = 'My dog';
- 9.2 Concatenate strings using the plus
+=
operator.
Operators are faster than function calls.
var name = 'Breno';
// Bad
name.concat('Polanski');
// Good
name += ' Polanski';
- 9.3 Prefer string concatenation for long strings, always adding line breaks after an operator.
Long strings can impact performance big time (benchmark and discussion).
// Bad
var errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
// Bad
var errorMessage = 'This is a super long error that was thrown because \
of Batman. When you stop to think about how Batman had anything to do \
with this, you would get nowhere \
fast.';
// Good
var errorMessage = 'This is a super long error that was thrown because ' +
'of Batman. When you stop to think about how Batman had anything to do ' +
'with this, you would get nowhere fast.';
- 9.4 Use
toString()
to convert a value toString
.
Be aware that
toString()
does not work withnull
andundefined
values.
var value = 42;
// Bad
value += ''; // '42'
// Good
value.toString(); // '42'
- 10.1 Do not use floating decimals.
Althought they're valid JavaScript, they make the code harder to read.
// Bad
var foo = .5;
var bar = -.7;
var baz = 2.;
// Good
var foo = 0.5;
var bar = -0.7;
var baz = 2.0;
- 11.1 Always use the function declaration form instead of function expressions.
// Bad
var sum = function(x, y) {
return x + y;
};
// Good
function sum(x, y) {
return x + y;
}
- 11.2 Favor named function expression instead of anonymous function expression.
This helps you to debug your code, since the dev tools will show the name of the context which the error has ocurred.
// Bad
var anonymous = function() {}
// Good
var named = function named() {}
- 11.3 Do not declare a function in a non-function block.
// Just don't
while(true) {
function test() {
return false;
}
}
- 11.4 Do not 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) {
// ...
}
- 11.5 Whenever you have more than
3
arguments being passed to a function use an object instead.
// Bad
function setUser(firstName, lastName, age, gender, occupation) {
// Too many function arguments
}
setUser('Jon', 'Snow', 25, 'Male', 'Night watcher');
// Good
// Use a config/options object instead
function setUser(user) {
// Do stuff
}
setUser({
firstName: 'Jon',
lastName: 'Snow',
age: 25,
gender: 'Male',
occupation: 'Night watcher'
});
- 11.6 Use
Function()
as no-op.
function(callback) {
setTimeout(callback || Function(), 2000);
}
- 11.7 Do not modify function parameter values.
Often, assignment to function parameters is unintended and indicative of a mistake or programmer error.
// Bad
function foo(bar) {
bar = 13;
}
// Good
function foo(bar) {
var baz = bar;
}
- 11.8 Make use of bind when you need to persist the value of this accross different scopes.
It removes the need of creating a new variable.
// Bad
function foo() {
var self = this;
function bar() {
self.something();
}
}
// Good
function foo() {
function bar() {
this.something();
}.bind(this);
}
- 12.1 Use strict equality to compare variable values.
Strict equality checks for both value and type which is why we expect.
// Bad
function isEmptyString (text) {
return text == '';
}
isEmptyString(0);
// <- true
// Good
function isEmptyString (text) {
return text === '';
}
isEmptyString(0);
// <- false
- 13.1 Ternary operators should only be used to compare booleans.
// Bad
items.length ? show() : hide();
// Really bad (I have no idea what's happening here)
items.length === 0 && settings.id === 'foo' ? hide() : show();
// Good
var isEmpty = items.length === 0;
var isFoo = settings.id === 'foo';
isEmpty && isFoo ? hide() : show();
// Even better
var shouldHide = isEmpty && isFoo;
shouldHide ? hide() : show();
- 13.2 Don't use double negation
!!
to test booleans.
// Bad
if (!!foo) {
// ...
}
// Good
if (foo) {
// ...
}
- 14.1 Always wrap blocks within braces and embrace new lines.
// Bad
if (false)
return;
// Bad (wrapped with braces but missing new line)
if (false) { return false; }
// Good
if (true) {
return true;
}
// Good
if (true) {
return true;
}
else {
return false;
}
// Bad
while(true)
doSomething();
// Good
while(true) {
doSomething();
}
- 15.1 Preferably bake
console
statements into a service that can easily be disabled in production. Alternatively, don't ship anyconsole.log
printing statements to production distributions.
-
16.1 Ensure your code is descriptive, well commented, and approachable by others. Great code comments convey context or purpose.
-
16.2 Using
FIXME
andTODO
tags can help other developers understand and maintain your code. In multiline comments, add a line break and place them at the end of the comment. -
16.3 Use documentation block syntax for multiline comments.
-
16.4 Use
//
for single line comments. Place them on a newline above the subject of the comment and add an empty line before the comment.
// Bad
/*
Used to match `RegExp` special characters.
See this [article on `RegExp` characters](http://www.regular-expressions.info/characters.html#special)
for more details.
TODO: Let the web service handle the validation.
FIXME: Bad performance.
*/
// Good
/**
* Used to match `RegExp` special characters.
* See this [article on `RegExp` characters](http://www.regular-expressions.info/characters.html#special)
* for more details.
*
* TODO: Let the web service handle the validation.
* FIXME: Bad performance.
*/
var matchSpecialChars = /[.*+?^${}()|[\]\/\\]/g;
// Bad (vague description and placed aside of variable declaration)
var isActive = true; // When is active
// Good (right position and descriptive comment)
// Boolean value to indicate whether the current session is active or not
var isActive = true;
// Bad (weird big chunk of code, everything is visually tied together)
function bootstrap() {
// Notifies if there's a session already running
if(isActive) {
console.log('Session is already active!');
}
isActive = true;
// Return session id so mediator can notify the controller
return sessionId;
}
// Good
function bootstrap() {
// Notifies if there's a session already running
if(isActive) {
console.log('Session is already active!');
}
isActive = true;
// Return session id so mediator can notify the controller
return sessionId;
}
- 17.1 Use
PascalCase
when naming constructors.
// Bad
function crewMember(name, role) {
this.name = name;
this.role = role;
}
var god = new crewMember('Tom Barber', 'Developer');
// Good
function CrewMember(name, role) {
this.name = name;
this.role = role;
}
var designer = new CrewMember('Tom Barber', 'Developer');
- 17.2 Avoid single letter names and abbreviations. Be descriptive and clear.
// Bad
function f() {
// What the hell is this?
}
// Good
function bootstrapFoo() {
// Ah, ok!
}
// Bad
function stuff() {
// "stuff" is too generic
}
// Good
function doAnimationStuff() {
// Now we're talking
}
// Bad (abbreviations)
var props = {};
var config = {};
// Better
var properties = {};
var settings = {};
// Not so good
function init() {
}
// Better
function initialize() {
}
- 17.3 Always close constructor invocations with parenthesis.
It's going to be easier to pass new constructor values if needed in the future.
// Bad
var foo = new FooBar;
// Good
var bar = new FooBar();
var baz = new FooBar(1, 'lorem');
- 17.4 Always use a leading underscore
_
when naming private properties and methods.
// Bad
var $name = 'Foo';
var __name = 'Bar';
// Good
var _name = 'Baz';
- 17.5 Booleans should start with "is", "has", or "should".
This give us a clear idea of what that variable is.
// Bad
var ready = true;
var animate = true;
var animation = true;
// Good
var isReady = true;
var shouldAnimate = true;
var hasAnimation = true;
- 17.6 When naming an acessor, start with
get
orset
. Also always name the getter argument asvalue
.
var currentStatus;
// Bad
function status(val) {
if (val) {
currentStatus = val;
}
return currentStatus;
}
// Good
function setStatus(value) {
currentStatus = value;
}
function getStatus() {
return currentStatus;
}
- 17.7 When naming an event handler, prefix
on
along with its event type.
// Bad
$('.button').click(click);
function click() {
console.log('meh');
}
// Good
$('.button').click(onClick);
function onClick() {
// Stuff happens here
}
- 17.8 Use uppercase when naming "constant likes" variables.
// Bad
var esc_key = 27;
var rightKey = 39;
// Good
var ESC_KEY = 27;
var RIGHT_KEY = 39;
- 18.1 Use soft tabs set to
2
spaces and never mix spaces with tabs.
// Bad
function() {
∙∙∙∙var name;
}
// Bad
function() {
∙var name;
}
// Bad
function() {
⇥var name;
}
// Good
function() {
∙∙var name;
}
- 18.2 Always add an empty line at the end of your file.
(function() {
document.write('Hello World!');
})();
↵
- 18.3 Place a space before and after conditions and loop declarations.
// Bad
if(false){return false;}
// Good
if (true) {
return true;
}
// Bad
while(false){console.log('whatever');}
// Good
while (false) {
console.log('alright!');
}
- 18.4 Set off operators with spaces.
// Bad
var x=y+5;
// Good
var x = y + 5;
- 18.5 Place a space after loop steps.
// Bad
for(var i=0;i<10;i++) {
console.log(i);
}
// Good
for (var i = 0; i < 42; i++) {
console.log(i);
}
- 18.6 Place a space after each function argument.
// Bad
function setUser(name,surname,age){
}
// Good
function setUser(name, surname, age) {
}
- 18.7 Objects properties should be split into new lines.
// Bad
var user = {name: 'John', surname: 'Doe', age: 42};
// Good
var user = {
name: 'John',
surname: 'Doe',
age: 42
};
// Bad
var setup = { foo: 'bar' };
// Good
var setup = {
foo: 'bar'
}
- 18.8 Use indentation when making long method chains.
// Bad
$('.js-items').find('.is-selected').highlight().end().find('.open').updateCount();
// Good
$('.js-items')
.find('.is-selected')
.highlight()
.end()
.find('.open')
.updateCount();
The typeof
operator returns a string indicating the type of the unevaluated operand.
- 19.1 String
typeof variable === 'string';
- 19.2 Number
typeof variable === 'number';
- 19.3 Boolean
typeof variable === 'boolean';
- 19.4 Object
typeof variable === 'object';
- 19.5 Array
Array.isArray(arrayLikeObject);
- 19.6 Node
elem.nodeType === 1;
- 19.7
null
variable === null;
- 19.8
undefined
typeof variable === 'undefined';
- 20.1 Always cache jQuery lookups.
// Bad
$('.js-item').text('Disabled item');
$('.js-item').addClass('is-disabled');
// Good
var $item = $('.js-item');
$item
.text('Disabled item')
.addClass('is-disabled');
- 20.2 Prefer
remove()
overempty()
.
remove()
is faster because it doesn't completely rewrite the DOM node.
- 20.3 Always favor jQuery helpers over third-party and custom stuff.
// Bad (importing Underscore/LoDash just to use `_.bind()`)
_.bind(someFunction, this);
// Good (instead use jQuery's `$.proxy()`)
$.proxy(someOtherFunction, this);
// Bad (writing `trim()` from scratch)
function trim(string) {
return string.replace(/\s/gm, '');
}
trim(' f oo '); // 'foo'
// Good (instead use jQuery's `$.trim()`)
$.trim(' f oo '); // 'foo'
We use ESLint to lint our JavaScript code. All the rules can be found on the .eslintrc
file.