- Banned features and gotchas
- Naming
- Whitespace
- Punctuation
- Comments
- Inline documentation
- Types
- Conditionals
- Loops and comprehensions
- Modules
- Resources
- License
with
eval
new Array
new Object
__proto__
- Modifying native objects or their prototypes
- The comma operator, except with
for
loops - Bit math and clever optimizations
JavaScript is full of them! Avoid being clever. If you wonder whether something works or need to read a line twice, rewrite with simpler syntax.
Avoid declaring functions inside of a block, such as a conditional. This is undefined behavior, and browsers handle it differently. Instead, assign the function to a variable.
// No
if (currentUser) {
function test() {
console.log('Nope.');
}
}
// Yes
if (currentUser) {
var test = function test() {
console.log('Yup.');
};
}
Avoid implicit type coercion, especially with ==
and !=
. Instead, always use ===
and !==
. The exception is that item == null
is OK.
Be careful when adding numbers to explicitly cast to a number type. Otherwise, you might accidentally perform string concatenation if one of the arguments is a string.
Use a null check of if (value == null)
instead of if (!value)
when value
can be any number. It is easy to forget that zero is falsey.
All JavaScript numbers are doubles. Be careful when using large integers, doing decimal math, and when casting numbers to and from strings. Bit operators act like 32-bit signed integers, which can lead to unexpected results.
NaN
is the only value in JavaScript that does not equal itself.
lowerCamelCase
- Local variables and functions as well as public properties or functions_underscoreCamelCase
- Internal properties or functionsUpperCamelCase
- Constructor functionsSCREAMING_SNAKE_CASE
- Constantsdashed-lower-case
- HTML ids, classes, and attribute names
When camelcasing acronyms, always treat them like words. For example: htmlSection
or newHtml
Avoid reserved words as property names.
Do not abbreviate variable or function names unless they are explicitly listed here. If an abbreviation is listed here, always use it instead of the full word.
Generic abbreviations:
err
- Error arguments in callbacks and catchcb
- Callbacki
- Indexlen
- Lengthe
- HTML event objectel
- HTML elementfn
- Variable representing a function
Abbreviations especially for use in Share and Racer:
op
- Operationdoc
- Data documentv
- Versiondb
- Database
When iterators are nested or functions that take a callback are nested, use specific names for the different callbacks or indices.
Use U.S. English spelling. For words with ambiguous spelling, use the spelling in the following list:
indices
, not indexesreferrer
, not referer, except in HTTP headers where it is customarily mispelled
- Two space indentation
- No trailing line whitespace
- No whitespace on empty lines
- Has extra linebreak at end of file
Indent statements continued between lines one level (two spaces). Place operators that continue a line at the end of the line.
greeting = (isNew) ?
'Welcome!' :
'Welcome back!';
farewell = 'Come back to our place soon ' +
user.firstName;
In chained method calls that don't fit on a single line, place each call on a separate line and indented by one level, with a leading .
.
expressApp
.use(express.logger())
.use(express.favicon())
.use(expressApp.router);
Do not vertically align items in consecutive lines.
// No
var x = 0;
var y = 0;
var position = 'top';
// No
var magicWords = [
'abracadabra'
'gesundheit'
'ventrilo'
];
// No
var message = (regExp.test(text)) ?
'You have a match!' :
'No match';
// Yes
var x = 0;
var y = 0;
var position = 'top';
// Yes
var magicWords = [
'abracadabra'
'gesundheit'
'ventrilo'
];
// Yes
var message = (regExp.test(text)) ?
'You have a match!' :
'No match';
Prefer single quoted strings (''
) instead of double quoted (""
) strings.
End all statements with semicolons.
Use leading commas with a single tab indentation before the comma when declaring arrays or objects.
var names = [
'Judy'
, 'Walt'
, 'Ben'
, 'Susan'
, 'Kim'
];
Use one var
for each variable declaration, and declare it where the variable is first used in the function. This makes it easier to refactor code and make sure that each variable is properly declared as local. Further, when initializing the same variable in independent sections, one should redeclare it in each section. This is especially desired for loop iterators.
// No
var keys = ['foo', 'bar']
, values = [23, 42]
, object = {}
, len
, key
, i
;
for (i = values.length; i--;) {
values[i] = values[i] * 2;
}
for (i = 0, len = values.length; i < len; i++) {
key = keys[i];
object[key] = values[i];
}
// Yes
var keys = ['foo', 'bar'];
var values = [23, 42];
for (var i = values.length; i--;) {
values[i] = values[i] * 2;
}
var object = {};
for (var i = 0, len = values.length; i < len; i++) {
var key = keys[i];
object[key] = values[i];
}
Capitalize the first word of the comment, unless the first word is an identifier that begins with a lower-case letter. If a comment is short, omit the period at the end. Use backticks to quote identifiers inside of comment descriptions.
Block comments apply to the block of code that follows them.
Each line of a block comment starts with a //
and a single space, and should be indented at the same level of the code that it describes.
Paragraphs inside of block comments are separated by a line containing //
.
// This is a block comment. Note that if this were a real block
// comment, we would actually be describing the proceeding code.
//
// This is the second paragraph of the same block comment. Note
// that this paragraph was separated from the previous paragraph
// by a line containing a single comment character.
start();
stop();
Place inline comments on the line immediately above the statements they are describing. Do not place inline comments on the same line unless it aids in clarity, such as documenting a multi-line regular expression.
The use of inline comments should be limited, because their existence is typically a sign of a code smell. Especially do not use inline comments when they state the obvious.
// No
// Increment x
x = x + 1;
However, inline comments can be useful in certain scenarios:
// Yes
// Compensate for border
x = x + 1;
Use annotations when necessary to describe a specific action that must be taken against the indicated block of code.
Write the annotation on the line immediately above the code that the annotation is describing.
Follow the annotation keyword by a colon and a space, and a descriptive note.
// FIXME: The client's current state should *not* affect payload processing.
resetClientState();
processPayload();
If multiple lines are required by the description, indent subsequent lines with two spaces:
// TODO: Ensure that the value returned by this call falls within a certain
// range, or throw an exception.
analyze();
Annotation types:
TODO
: describe missing functionality that should be added at a later dateFIXME
: describe broken code that must be fixedHACK
: describe the use of a questionable coding practiceOPTIMIZE
: describe code that is inefficient and may become a bottleneckREVIEW
: describe code that should be reviewed to confirm implementationREFACTOR
: describe code that should be refactored
Use JSDoc to document functions and types inline. All public interfaces should be annotated.
Installing DocBlockr is recommended for Sublime Text.
/**
* A tasty treat with a hole
* @param {string} flavor Name of the flavor
* @constructor
*/
function Donut(flavor) {
this.flavor = flavor;
}
/**
* Decorate a donut
* @param {Donut} donut The donut to decorate
* @param {Object} [options] Options for decorating the donut
* @param {boolean} [options.needsSprinkles] Whether the donut needs sprinkles
* @param {boolean} [options.needsGlaze] Whether the donut needs glaze
* @return {Donut} The decorated donut
*/
function decorateDonut(donut, options) {
// ...
}
V8 and modern JavaScript compilers can better optimize code when objects follow more regular type structures. In addition, Chrome's memory profiling tools provide more rapid debugging and more useful insights when objects are of named types.
Thus, when objects are created with a regular set of properties, using a constructor is preferred to object literals. This is especially important for long-lived objects and objects that act as maps, since these are more likely to be involved in memory leaks. However, using object literals is OK for boxing up multiple arguments to get immediately parsed out and irregular blobs of data.
For performance reasons, the properties of a typed object should not be changed after instantiation. New properties should not be added dynamically, and properties should not be removed with a delete
statement. Instead, properties that are only sometimes existant should be initialized to null
in the constructor and cleared via a set to null
. Adding properties dynamically and delete
should ideally only be used on objects that act as maps.
Arrays should be avoided as containers of mixed types; they should only be used to represent lists of a single type.
// No
// Constructors are preferred even for singleton objects and simple map
// objects. This gives them type names in the memory profiler
var library = {
isbnMap: {}
};
// Prototypes are preferred to dynamically adding methods
library.add = function(book) {
this.isbnMap[book.isbn] = book;
// Avoid dynamically adding or removing properties of a typed object
book.currentLibrary = this;
};
// A constructor should be used for greater efficiency, better profiling,
// and to make it more explicit what are the optional and required fields
library.add({
isbn: '978-1470178192'
, title: 'Moby Dick'
, author: 'Herman Melville'
});
// Yes
function Book(isbn, options) {
options || (options = {});
this.isbn = isbn;
this.title = options.title;
this.author = options.author;
// Make sure to initialize all properties that a typed object can have in
// the constructor. Set properties to `null` if there is no default value
this.currentLibrary = null;
}
function IsbnMap() {}
function Library() {
this.isbnMap = new IsbnMap;
}
Library.prototype.add = function(book) {
this.isbnMap[book.isbn] = book;
book.currentLibrary = this;
}
var library = new Library;
// This options object literal is fine, because it is immediately parsed
// out and can be easily garbage collected
var book = new Book('978-1470178192', {
title: 'Moby Dick'
, author: 'Herman Melville'
});
library.add(book);
Omit curly braces for if
statements that fit on one line and that don't have an else
. Always write if
statements that have an else
on multiple lines and use curly braces.
// No
if (isNew) onNew();
else onOld();
// No
if (isNew)
onNew();
else
onOld();
// Yes
if (isNew) {
onNew();
} else {
onOld();
}
// Yes
if (err) callback(err);
To avoid nesting of if
statements, always return a function's value as early as possible.
// No
function isPercentage(val) {
if (val >= 0) {
if (val < 100) {
return true;
} else {
return false;
}
} else {
return false;
}
}
// Yes
function isPercentage(val) {
if (val < 0) return false;
if (val > 100) return false;
return true;
}
Lever's suggestions on loops:
Prefer for
loops and not Array#forEach in cases where a closure wrapper is not needed.
I kind of like the cleanliness of using Array#forEach in many cases but appreciate the simplicity and speed of just always using for
- What do you guys think?
// No
items.forEach(function(item) {
console.log(item);
});
// Yes
for (var i = 0, len = items.length; i < len; i++) {
var item = items[i];
console.log(item);
}
Like returning early from a function, use continue
to avoid nesting of conditionals in loops.
// No
var results = [];
for (var i = 0, len = values.length; i < len; i++) {
var value = values[i];
if (value >= threshold) {
results.push(value);
}
}
// Yes
var results = [];
for (var i = 0, len = values.length; i < len; i++) {
var value = values[i];
if (value < threshold) continue;
results.push(value);
}
Use the following canonical forms of loops:
// Loop forwards
for (var i = 0, len = items.length; i < len; i++) {
var item = items[i];
// ...
}
// Loop backwards
for (var i = items.length; i--;) {
var item = items[i];
// ...
}
// Loop until undefined
while (node.firstChild) {
node.removeChild(node.firstChild);
}
// Loop over Node.childNodes (about 3x faster than iterating through childNodes by index)
for (var node = parent.firstChild; node; node = node.nextSibling) {
// ...
}
// Loop over Element.children (about 3x faster than iterating through children by index)
for (var el = parent.firstElementChild; el; el = el.nextElementSibling) {
// ...
}
Use the Node.js module convention for both server and client code. Each module is a file. Modules may export an object with stateless methods, a single function, or a constructor.
All files should be written in the following order:
- Require statements
- Constant definitions
- Exports declarations
- Implementation
Place require statements at the top of a file. Group them in the following order:
- Standard library imports
- Third party library imports
- Local imports
When using imported functions, don't destructure them at the top of the file. This can make it harder for others to figure out the context of the function when they are reading the code later.
// No
var join = require('path').join;
...
join(__dirname, '/foo');
// Yes
var path = require('path');
...
path.join(__dirname, '/foo');
Modules that export an object have a camel case name starting with a lowercase letter. Where either singular or plural would make sense, pluaral names are preferred.
They only export stateless functions and global constants shared among all instances of the module. Public exported functions start with a lowercase letter, and functions exported for internal use start with an underscore.
Modules that export an object only use exports
and they do NOT use module.exports
. All exported functions are exported where they are defined. Use of exported functions in the same file also refer to the function as exports.xxx
.
fruits.js
var fs = require('fs');
var util = require('util');
var TIMEOUT = 1000;
module.exports = {
TIMEOUT: TIMEOUT
, ripen: ripen
, ripenEach: ripenEach
};
function ripen(fruit, amount) {
fruit.ripeness *= amount;
}
function ripenEach(fruits, amount) {
for (var i = 0, len = fruits.length; i < len; i++) {
ripen(fruit, amount);
}
}
Modules that export a function or constructor are named the same thing as the function they export. Modules exporting a constructor start with an uppercase character.
They always export using module.exports = <name>
.
Fruit.coffee
var fs = require('fs');
var util = require('util');
var TIMEOUT = 1000;
module.exports = Fruit;
Fruit.TIMEOUT = TIMEOUT;
function Fruit(name) {
this.name = name;
this.ripeness = 0;
}
Fruit.prototype.isRipe = function() {
return this.ripeness > 0.5;
};
- Felix's Node.js Style Guide
- npm Coding Style
- Google JavaScript Style Guide
- jQuery JavaScript Style Guide
- Airbnb JavaScript Style Guide
- Principles of Writing Consistent, Idiomatic JavaScript
- Crockford's Code Conventions for the JavaScript Programming Language
- Dojo Style Guide
- On Layout & Web Performance
- String vs Array Concat
- Try/Catch Cost In a Loop
- Bang Function
- jQuery Find vs Context, Selector
- innerHTML vs textContent for script text
- Long String Concatenation
- JavaScript: The Good Parts - Douglas Crockford
- JavaScript Patterns - Stoyan Stefanov
- Pro JavaScript Design Patterns - Ross Harmes and Dustin Diaz
- High Performance Web Sites: Essential Knowledge for Front-End Engineers - Steve Souders
- Maintainable JavaScript - Nicholas C. Zakas
- JavaScript Web Applications - Alex MacCaw
- Pro JavaScript Techniques - John Resig
- Smashing Node.js: JavaScript Everywhere - Guillermo Rauch
- DailyJS
- JavaScript Weekly
- JavaScript, JavaScript...
- Bocoup Weblog
- Adequately Good
- NCZOnline
- Perfection Kills
- Ben Alman
- Dmitry Baranovskiy
- Dustin Diaz
- nettuts
(The MIT License)
Copyright (c) 2013 Lever
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
This document forked from the Airbnb JavaScript Style Guide.
(The MIT License)
Copyright (c) 2012 Airbnb
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.