JS Coding Standards
Javascript
Early ES6 adoption for MOS devs
Some ES6 features we should be using are the use of let
and const
. These variable declarations work on all modern browsers and even as far back as IE11.
- First, watch this video on
var
vsconst
vslet
: Video - The above video shows great examples of how and when to use these.
- Use
const
orlet
for all of your references; avoid usingvar
.
Why? This ensures that you can’t reassign your references, which can lead to bugs and difficult to comprehend code.
// bad
var a = 1;
var b = 2;
// good
const a = 1;
const b = 2;
- If you must reassign references, use
let
instead ofvar
.
Why?
let
is block-scoped rather than function-scoped likevar
.
// bad
var count = 1;
if (true) {
count += 1;
}
// good, use the let.
let count = 1;
if (true) {
count += 1;
}
- Note that both let and const are block-scoped.
// const and let only exist in the blocks they are defined in.
...{
let a = 1;
const b = 1;
}
console.log(a); // ReferenceError
console.log(b); // ReferenceError
Whitespace & Indenting
- All code MUST indent using two (2) space characters
// bad
function foo() {
∙∙∙∙let name;
}
// bad
function bar() {
∙let name;
}
// good
function baz() {
∙∙let name;
}
- Place 1 space before the leading brace.
// bad
function test(){
console.log('test');
}
// good
function test() {
console.log('test');
}
// bad
dog.set('attr',{
age: '1 year',
breed: 'Bernese Mountain Dog',
});
// good
dog.set('attr', {
age: '1 year',
breed: 'Bernese Mountain Dog',
});
- Place 1 space before the opening parenthesis in control statements (if, while etc.). Place no space between the argument list and the function name in function calls and declarations.
// bad
if(isJedi) {
fight ();
}
// good
if (isJedi) {
fight();
}
// bad
function fight () {
console.log ('Swooosh!');
}
// good
function fight() {
console.log('Swooosh!');
}
- Set off operators with spaces.
// bad
const x=y+5;
// good
const x = y + 5;
- Use indentation when making long method chains (more than 2 method chains). Use a leading dot, which emphasizes that the line is a method call, not a new statement.
// bad
$('#items').find('.selected').highlight().end().find('.open').updateCount();
// bad
$('#items').
find('.selected').
highlight().
end().
find('.open').
updateCount();
// good
$('#items')
.find('.selected')
.highlight()
.end()
.find('.open')
.updateCount();
// bad
const leds = stage.selectAll('.led').data(data).enter().append('svg:svg').classed('led', true)
.attr('width', (radius + margin) * 2).append('svg:g')
.attr('transform', `translate(${radius + margin},${radius + margin})`)
.call(tron.led);
// good
const leds = stage.selectAll('.led')
.data(data)
.enter().append('svg:svg')
.classed('led', true)
.attr('width', (radius + margin) * 2)
.append('svg:g')
.attr('transform', `translate(${radius + margin},${radius + margin})`)
.call(tron.led);
// good
const leds = stage.selectAll('.led').data(data);
- Do not add spaces inside parentheses.
// bad
function bar( foo ) {
return foo;
}
// good
function bar(foo) {
return foo;
}
// bad
if ( foo ) {
console.log(foo);
}
// good
if (foo) {
console.log(foo);
}
- Do not add spaces inside brackets.
// bad
const foo = [ 1, 2, 3 ];
console.log(foo[ 0 ]);
// good
const foo = [1, 2, 3];
console.log(foo[0]);
- Add spaces inside curly braces.
// bad
const foo = {clark:'kent'};
// good
const foo = {clark: 'kent'};
Semi-colons
'''Yes, use them.''' JavaScript allows optional "semi-colon insertion". Our standards do not.
- All statements (except
for, function, if, switch, try, while
) MUST be followed by a semi-colon (;) - Return values MUST start on the same line as the
return
keyword.
Examples:
// bad - raises exception
const luke = {}
const leia = {}
[luke, leia].forEach(jedi => jedi.father = 'vader')
// bad - raises exception
const reaction = "No! That's impossible!"
(async function meanwhileOnTheFalcon() {
// handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
// ...
}())
// bad - returns `undefined` instead of the value on the next line - always happens when `return` is on a line by itself because of ASI!
function foo() {
return
'search your feelings, you know it to be foo'
}
// good
const luke = {};
const leia = {};
[luke, leia].forEach((jedi) => {
jedi.father = 'vader';
});
// good
const reaction = "No! That's impossible!";
(async function meanwhileOnTheFalcon() {
// handle `leia`, `lando`, `chewie`, `r2`, `c3p0`
// ...
}());
// good
function foo() {
return 'search your feelings, you know it to be foo';
}
Semi-colon Exception:
- Anonymous functions assigned to a variable MUST be followed by a semi-colon.
const doSomething = function(context) {
// Statements...
}; // <-- MUST include semi-colon.
Drupal.behaviors.tableSelect = function(context) {
// Statements...
}; // <-- MUST include semi-colon.
$(window).load(function() {
// Statements...
}); // <-- MUST include semi-colon.
Commas
- Leading commas: '''Nope.'''
// bad
const story = [
once
, upon
, aTime
];
// good
const story = [
once,
upon,
aTime,
];
// bad
const hero = {
firstName: 'Ada'
, lastName: 'Lovelace'
, birthYear: 1815
, superPower: 'computers'
};
// good
const hero = {
firstName: 'Ada',
lastName: 'Lovelace',
birthYear: 1815,
superPower: 'computers',
};
- Additional trailing comma: '''Yup.'''
Why? This leads to cleaner git diffs.
// bad - git diff without trailing comma
const hero = {
firstName: 'Florence',
- lastName: 'Nightingale'
+ lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing']
};
// good - git diff with trailing comma
const hero = {
firstName: 'Florence',
lastName: 'Nightingale',
+ inventorOf: ['coxcomb chart', 'modern nursing'],
};
// bad
const hero = {
firstName: 'Dana',
lastName: 'Scully'
};
const heroes = [
'Batman',
'Superman'
];
// good
const hero = {
firstName: 'Dana',
lastName: 'Scully',
};
const heroes = [
'Batman',
'Superman',
];
Comments
- Comments should end with a full stop (period, exclamation, question mark etc.)
- Use
/** ... */
for multi-line 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 single line comments on a newline above the subject of the comment. Put an empty line before the comment unless it’s 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
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 it easier to read.
// 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;
}
- Prefixing your comments with
FIXME
orTODO
helps other developers quickly understand if you're pointing out a problem that needs to be revisited, or if you're suggesting a solution to the problem that needs to be implemented. These are different than regular comments because they are actionable. The actions areFIXME: -- need to figure this out
orTODO: -- need to implement
. - 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;
}
}
Objects
- Use the literal syntax for object creation.
// bad
const item = new Object();
// good
const item = {};
- Only quote properties that are invalid identifiers. eslint: quote-props jscs: disallowQuotedKeysInObjects
Why? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines.
// bad
const bad = {
'foo': 3,
'bar': 4,
'data-blah': 5,
};
// good
const good = {
foo: 3,
bar: 4,
'data-blah': 5,
};
Blocks
- Use braces with all multi-line blocks.
// bad
if (test)
return false;
// good
if (test) return false;
// good
if (test) {
return false;
}
// bad
function foo() { return false; }
// good
function bar() {
return false;
}
- If you're using multi-line blocks with
if
andelse
, putelse
on the same line as yourif
block’s closing brace.
// bad
if (test) {
thing1();
thing2();
}
else {
thing3();
}
// good
if (test) {
thing1();
thing2();
} else {
thing3();
}
- If an
if
block always executes areturn
statement, the subsequentelse
block is unnecessary. Areturn
in anelse if
block following anif
block that contains areturn
can be separated into multipleif
blocks.
// bad
function foo() {
if (x) {
return x;
} else {
return y;
}
}
// bad
function cats() {
if (x) {
return x;
} else if (y) {
return y;
}
}
// bad
function dogs() {
if (x) {
return x;
} else {
if (y) {
return y;
}
}
}
// good
function foo() {
if (x) {
return x;
}
return y;
}
// good
function cats() {
if (x) {
return x;
}
if (y) {
return y;
}
}
//good
function dogs(x) {
if (x) {
if (z) {
return y;
}
} else {
return z;
}
}
jQuery
- Prefix jQuery object variables with a
$
.
// 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',
});
}