Use /** ... */ for multiline comments.
// bad
// make() returns a new element
// based on the passed in tag name
//
// @param {String} tag
// @return {Element} element
function make(tag) {
// ...
return element;
}
// good
/**
* make() returns a new element
* based on the passed-in tag name
*/
function make(tag) {
// ...
return element;
}
Use // for single-line comments. Place the comment on a newline above the subject. Put an empty line before the comment unless it is on the first line of a block.
// bad
const active = true; // is current tab
// good
// is current tab
const active = true;
// bad
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
const type = this.type || 'no type';
return type;
}
// good
function getType() {
console.log('fetching type...');
// set the default type to 'no type'
const type = this.type || 'no type';
return type;
}
// also good — comment on first line of block needs no preceding blank line
function getType() {
// set the default type to 'no type'
const type = this.type || 'no type';
return type;
}
Start all comments with a space to make them easier to read. eslint: spaced-comment
// bad
//is current tab
const active = true;
// good
// is current tab
const active = true;
// bad
/**
*make() returns a new element
*based on the passed-in tag name
*/
function make(tag) {
// ...
return element;
}
// good
/**
* make() returns a new element
* based on the passed-in tag name
*/
function make(tag) {
// ...
return element;
}
FIXME and TODO
Prefix action item comments with FIXME or TODO to distinguish them from explanatory comments. These markers are actionable and can be picked up by tooling.
FIXME marks a problem that needs to be revisited. TODO marks a solution that needs to be implemented.
Use // FIXME: to annotate problems:
class Calculator extends Abacus {
constructor() {
super();
// FIXME: shouldn't use a global here
total = 0;
}
}
Use // TODO: to annotate solutions to problems:
class Calculator extends Abacus {
constructor() {
super();
// TODO: total should be configurable by an options param
this.total = 0;
}
}
Events
When attaching data payloads to events — whether DOM events or something more proprietary like Backbone events — pass an object literal (a “hash”) instead of a raw value. This allows a subsequent contributor to add more data to the event payload without finding and updating every handler.
// bad
$(this).trigger('listingUpdated', listing.id);
// ...
$(this).on('listingUpdated', (e, listingID) => {
// do something with listingID
});
// good
$(this).trigger('listingUpdated', { listingID: listing.id });
// ...
$(this).on('listingUpdated', (e, data) => {
// do something with data.listingID
});
Passing a hash means you can add new properties to the payload later without changing the event signature or breaking existing handlers.
jQuery
Prefix jQuery variables with $
// bad
const sidebar = $('.sidebar');
// good
const $sidebar = $('.sidebar');
// good
const $sidebarBtn = $('.sidebar-btn');
Cache jQuery lookups
// bad
function setSidebar() {
$('.sidebar').hide();
// ...
$('.sidebar').css({
'background-color': 'pink',
});
}
// good
function setSidebar() {
const $sidebar = $('.sidebar');
$sidebar.hide();
// ...
$sidebar.css({
'background-color': 'pink',
});
}
Scope DOM queries
For DOM queries, use cascading $('.sidebar ul') or parent > child $('.sidebar > ul').
Use find with scoped jQuery object queries:
// bad
$('ul', '.sidebar').hide();
// bad
$('.sidebar').find('ul').hide();
// good
$('.sidebar ul').hide();
// good
$('.sidebar > ul').hide();
// good
$sidebar.find('ul').hide();
Standard Library
The Standard Library contains utilities that are functionally broken but remain for legacy reasons. Prefer the safer Number namespace equivalents.
Number.isNaN over isNaN
eslint: no-restricted-globals
The global isNaN coerces non-numbers to numbers, returning true for anything that coerces to NaN. This leads to surprising results.
// bad
isNaN('1.2'); // false
isNaN('1.2.3'); // true
// good
Number.isNaN('1.2.3'); // false
Number.isNaN(Number('1.2.3')); // true
Number.isFinite over isFinite
eslint: no-restricted-globals
The global isFinite coerces non-numbers to numbers, returning true for anything that coerces to a finite number.
// bad
isFinite('2e3'); // true
// good
Number.isFinite('2e3'); // false
Number.isFinite(parseInt('2e3', 10)); // true
Testing
Write tests.
- Whichever testing framework you use, you should be writing tests.
- Strive to write many small pure functions, and minimize where mutations occur.
- Be cautious about stubs and mocks — they can make your tests more brittle.
- Airbnb primarily uses
mocha and jest. tape is also used occasionally for small, separate modules.
- 100% test coverage is a good goal to strive for, even if it is not always practical to reach it.
- Whenever you fix a bug, write a regression test. A bug fixed without a regression test is almost certainly going to break again in the future.