Skip to main content

Comments

Multi-line comments

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;
}

Single-line comments

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 comments with a space

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.

Build docs developers (and LLMs) love