This is a guide for writing JavaScript code in any environment. It is inspired by pragmatic thought leaders in the community and decisions from working with JavaScript in Node and in the browser day-in-day-out.
This guide was forked from Felix Geisendörfer's guide and is licensed under the CC BY-SA 3.0 license. The original guide is located here. You are encouraged to fork this repository and make adjustments according to your preferences.
There is a jshint options json file in this repo to help you conform to some of the guidelines outlined here.
git clone git://github.com/bengourley/js-style-guide ~/jshint-options
ln -s ~/jshint-options/jshint-options.json ~/.jshintrc
Putting a copy in your project root will ensure that the project always has the same jshint config even if your global .jshintrc config changes. Also it allow you to put project specific predefs that don't belong in your global.
curl https://raw.github.com/bengourley/js-style-guide/master/jshint-options.json > .jshintrc
git add .jshintrc
git commit -m 'Adding .jshintrc from github.com/bengourley/js-style-guide' .jshintrc
Use 2 spaces for indenting your code and swear an oath to never mix tabs and spaces in your code - a special kind of hell is awaiting you otherwise.
Follow Crockford’s rules about spaces and be consistent:
- No space between and named function and its brackets:
function foo() {}
- Exactly one space between an annonymous function and its brackets:
function () {}
- Exactly one space after a comma:
console.log(a, b, c)
- Exactly one space each side of infix operators:
5 + 5 / 3
,10 * Math.min(a, b)
- No space between a prefix/postfix operator and its operand:
-8 / 2
,i++
- No space after a property name and exactly one space after it in an object literal:
foo: bar
- Don't indent case statements inside switch statements.
Right:
switch (val) {
case 1:
console.log(val)
break;
default:
console.log('I only take 1')
}
Wrong:
switch (val) {
case 1:
console.log(val)
break;
default:
console.log('I only take 1')
}
Just like you brush your teeth after every meal, you clean up any trailing whitespace in your JS files before committing. Otherwise the rotten smell of careless neglect will eventually drive away contributors and/or co-workers.
Semicolons should be omitted for brevity and reduced visual noise.
Semicolons are unnecessary and all JavaScript implementations follow the same automatic semicolon injection rules to-the-T (yes, including IE6). Removing them makes the code easier to write and easier to read.
There are exceptional circumstances when lines should start with a semicolon. However, these circumstances are not common, and you should ratify your decision to use one before beginning a line with a semicolon.
Here is Isaacs' run down on those circumstances (verbatim, from here):
for (;;)
loops. They're actually required.- null loops like:
while (something) ;
(But you'd better have a good reason for doing that.) case "foo": doSomething(); break
- In front of a leading
(
or[
at the start of the line. This prevents the expression from being interpreted as a function call or property access, respectively.
Some examples of good semicolon usage:
;(x || y).doSomething()
;[a, b, c].forEach(doSomething)
for (var i = 0; i < 10; i++) {
doSomething()
}
Note that starting lines with - and + also should be prefixed with a semicolon, but this is much less common.
To Recap: If a line begins with [
, (
, +
or -
, you should start it with a semicolon.
Limit your lines to 80 characters and be strict about it. Yes, screens have gotten much bigger over the last few years, but your brain has not. Use the additional room for split screen, your editor supports that, right?
Use single quotes, unless you are writing JSON.
Right:
var foo = 'bar'
Wrong:
var foo = "bar"
Your opening braces go on the same line as the statement.
Right:
if (true) {
console.log('winning')
}
Wrong:
if (true)
{
console.log('losing')
}
Also, notice the use of whitespace before and after the condition statement.
Blocks containing a single statement may appear on one line, if desired. Curly
braces should be omitted. Blocks containing more than one statement (and an if
that has any else if
s or an else
) should always appear on multiple lines with
braces. This is because the most important tokens should
always appear on the left.
Right:
if (true) console.log('winning')
if (true) {
console.log('winning')
} else {
console.log('losing')
}
Wrong:
if (true) { console.log('winning') }
if (true) console.log('winning') else console.log('losing')
Declare many variables per var statement as it is less to type, and less visual noise.
Right:
var keys = [ 'foo', 'bar' ]
, values = [ 23, 42 ]
, object = {}
, key
while (items.length) {
key = keys.pop()
object[key] = values.pop()
}
Wrong:
var keys = [ 'foo', 'bar' ]
var values = [ 23, 42 ]
var object = {};
while (items.length) {
var key = keys.pop()
object[key] = values.pop()
}
Variables, properties and function names should use lowerCamelCase
. It is
most preferrable to use a single word for a variable name. Make an effort to
choose a good balance between meaningfull-ness and brevity. Single character
variables should generally be avoided, but are occasionally acceptable.
Right:
var admins = db.query('SELECT * FROM users ...');
Wrong:
var admin_user = db.query('SELECT * FROM users ...')
var adminUsersFromDb = db.query('SELECT * FROM users ...')
Constructors should be capitalized using UpperCamelCase
(also known as
Pascal Case).
Right:
function BankAccount() {
}
Wrong:
function bank_Account() {
}
Constants should be declared as regular variables or static class properties, using all uppercase letters.
Node.js / V8 actually supports mozilla's const extension, but unfortunately that cannot be applied to class members, nor is it part of any ECMA standard.
Right:
var SECOND = 1 * 1000
function File() {
}
File.FULL_PERMISSIONS = 0777
Wrong:
const SECOND = 1 * 1000
function File() {
}
File.fullPermissions = 0777
Use comma first style and put short declarations on a single line. Only quote keys when your interpreter complains:
Right:
var a = [ 'hello', 'world' ]
var b =
{ good: 'code'
, 'is generally': 'pretty'
}
Wrong:
var a = [
'hello', 'world'
]
var b = {
"good": 'code',
is generally: 'pretty'
};
Right:
var required =
[ 'main-nav'
, 'local-news-selector'
, 'notifications'
, 'datepicker'
]
Wrong:
var required = [
'main-nav',
'local-news-selector',
'notifications',
'datepicker',
]
For an explanation on why to use comma-first, look no further than this gist by Isaacs. Deviate slightly from the npm style here about the level of indentation. The npm style is to line up with where the opening symbol naturally occurs:
Wrong:
var myObj = { propA: 10
, proB: 20
}
It is better to have consistent levels of indenation, which means you don’t have to maintain all those indents if you rename the variable. Break a line and indent before the start symbol:
Right:
var myObj =
{ propA: 10
, proB: 20
}
Programming is not about remembering stupid rules. Use the triple equality operator as it will work just as expected.
Right:
var a = 0
if (a === '') {
console.log('winning')
}
Wrong:
var a = 0
if (a == '') {
console.log('losing')
}
The ternary operator should be used on a single line if it will fit within the 80 char limit. Split it up into multiple lines if it doesn't.
Right:
var foo = (a === b) ? 1 : 2
Wrong:
var foo = (aReallyLongVariableName.someProperty === bReallyLongVariableName.someProperty) ? 1 : 2
Do not extend the prototype of native JavaScript objects. Unless you are writing or using a 'polyfill' to simulate standard behaviour in old environments.
Right:
var a = []
if (!a.length) {
console.log('winning')
}
Wrong:
Array.prototype.empty = function () {
return !this.length
}
var a = []
if (a.empty()) {
console.log('losing')
}
Any non-trivial conditions should be assigned to a descriptive variable:
Right:
var isAuthorized = (user.isAdmin() || user.isModerator())
if (isAuthorized) {
console.log('winning')
}
Wrong:
if (user.isAdmin() || user.isModerator()) {
console.log('losing')
}
Keep your functions short. A good function fits on a slide that the people in the last row of a big room can comfortably read. So don't count on them having perfect vision and limit yourself to ~15 lines of code per function.
Functions should aim to have less than 5 parameters. 5 is the most that any should have. If you find yourself wanting more paramaters, you should generally use an options hash instead.
Good:
function animate(el, options, cb) {
...
}
Bad:
function animate(el, duration, ease, delay, properties, cb) {
...
}
There are times when a 1,000+ line file is needed but as a general rule of thumb try and keep your file < 300 lines. More than 500 lines and it's starting to be a code smell.
To avoid deep nesting of if-statements, always return a functions value as early as possible.
Right:
function isPercentage(val) {
if (val < 0) {
return false
}
if (val > 100) {
return false
}
return true
}
Wrong:
function isPercentage(val) {
if (val >= 0) {
if (val < 100) {
return true
} else {
return false
}
} else {
return false
}
}
Or for this particular example it may also be fine to shorten things even further:
function isPercentage(val) {
var isInRange = (val >= 0 && val <= 100)
return isInRange
}
Use closures, but don't nest them too much. Otherwise your code will become a mess. Your 80 char line limit should help prevent you nesting too far.
Right:
setTimeout(function () {
client.connect(afterConnect)
}, 1000)
function afterConnect() {
console.log('winning')
}
Wrong:
setTimeout(function () {
client.connect(function () {
console.log('losing')
})
}, 1000)
Inline comments should be line comments: //
, regardless of how many lines
they span, eg:
// Only run on browsers that support media queries.
// If a browser doesn't support media queries, .mq()
// will always return false.
if (!window.Modernizr.mq('(min-width:0px)')) return
breakPoints.push(new BreakPoint(name, media).check())
Comments describing the functionality of a method or function should be
multi-line comments: /* */
, eg:
/*
* Sanitizes a file-type extension
*/
function getFormat(f) {
switch (f) {
case '.jpg': return 'jpeg'
case '.gif': return 'gif'
default: return 'png'
}
}
Try to write comments that explain higher level mechanisms or clarify difficult segments of your code. Don't use comments to restate trivial things. Be terse, to the point and don't waffle.
Right:
// 'ID_SOMETHING=VALUE' -> [ 'ID_SOMETHING=VALUE'', 'SOMETHING', 'VALUE' ]
var matches = item.match(/ID_([^\n]+)=([^\n]+)/))
/*
* This function has a nasty side effect where a failure to increment a
* redis counter used for statistics will cause an exception. This needs
* to be fixed in a later iteration.
*/
function loadUser(id, cb) {
// ...
}
var isSessionValid = (session.expires < Date.now())
if (isSessionValid) {
// ...
}
Wrong:
// Execute a regex
var matches = item.match(/ID_([^\n]+)=([^\n]+)/))
// Usage: loadUser(5, function () { ... })
function loadUser(id, cb) {
// ...
}
// Check if the session is valid
var isSessionValid = (session.expires < Date.now())
// If the session is valid
if (isSessionValid) {
// ...
}
Use actual Error objects for errors, not strings or any other type of object. Justification here.
Don't throw
in asynchronous code.
Right
if (unicorns.length < 10) {
callback(new Error('not enough unicorns'))
}
Wrong:
if (unicorns.length < 10) {
callback('not enough unicorns')
}
if (unicorns.length < 10) {
callback('not enough unicorns')
}
if (unicorns.length < 10) {
throw 'not enough unicorns'
} else {
callback(null, unicorns)
}
Writing asynchronous functions with a callback, the callback's signature should
always be function (err, data) { ... }
.
To define a function in the current scope, use a function declaration. Don't assign the result of a function expression to a variable.
Right:
function greet() {
console.log('hello')
}
Wrong:
var greet = function () {
console.log('hello')
}
When writing in a node style environment, export only one thing from your module. In general, if you are assigning multiple exports, you have designed a poor API or you have written several modules that should be separated out.
Put your module exports as close to the top of the file as possible. This helps someone reading your module to know what is being exported.
Right:
module.exports = jim
Wrong:
module.exports.jim = jim
module.exports.bob = bob
module.exports.barry = barry
Put as many require
s as possible in the main body of your module (so that it runs
when it is loaded).
If something is missing you want to know about it when your program starts up and not when it's been running in production for 48hours before hitting a certain code path.
Right:
module.exports = doSomething
var async = require('async')
function doSomething() {
async.map(...)
}
Wrong:
module.exports = doSomething
function doSomething() {
require('async').map(...)
}
Crazy shit that you will probably never need. Stay away from it.
Do not use setters, they cause more problems for people who try to use your software than they can solve.
Feel free to use getters that are free from side effects, like providing a length property for a collection class.
The type attribute
is unnecessary and obsolete, so don't use it. //<![CDATA[
is required if you want your document to parse as XML. You should be using
the HTML5 DOCTYPE, in which case your document will parse as HTML meaning that
//<![CDATA[
is unnecessary.
Right:
<script>
console.log('Boo')
</script>
Wrong:
<script type="text/javascript">//<![CDATA[
console.log('Boo')
//]]></script>
Minimising the number of HTTP requests and file sizes will make your site faster.
The browser environment has no native module system. Node has a good one, so simlulate that with module.js.