JavaScript Style guide

Follow the regular WordPress JavaScript style guide, with the additions and modifications listed here.

Code can be checked against these coding standards using the ESLint coding standards configuration, available on npm as humanmade/coding-standards. This is also automatically run when running phpcs checks.



ES6 Syntax

ES6 syntax should be used wherever it makes sense, compiled to ES5 syntax for browser compatibility. This includes classes, arrow functions, and module import/exports.

Classes

ES6 includes support for first-class classes, and these should be used instead of the ES5-style of declaring a constructor function and extending the prototype.

For example, in ES5:

var MyClass = function () {
	this.something = 0;
};
MyClass.prototype.add = function () {
	this.something++;
};

In ES6, this is instead written:

class MyClass {
	constructor() {
		this.something = 0;
	}

	add() {
		this.something++;
	}
}

Methods should have one empty line between them (i.e. }\n\n).

Arrow functions

Arrow functions are shorter forms of closures/anonymous functions that automatically bind to the existing context.

For example, in ES5:

window.setTimeout( this.update.bind( this ) );
// or:
window.setTimeout( ( function () {
	this.update();
} ).bind( this ) );

In ES6, this is instead written:

window.setTimeout( () => {
	this.update();
});

For one-liners, this should be shortened to drop the braces:

window.setTimeout( () => this.update() );

The shortened form only allows one statement, and the result of this is automatically returned. This can be used for functional programming:

var sorted = items.map( item => item.id ).sort( id => id );

Avoid using .bind(this).

Rather than using func.bind(this), use a short arrow function. This is clearer to understand, as it uses regular callbacks rather than meta-programming.

// Bad:
something( this.handle.bind( this ) );

// Good:
something( () => this.handle() );

.bind() may be used if binding another object. For argument binding, arrow functions are again preferred:

// Bad:
var newHandle = this.handle.bind(this, 1, 2);

// Good:
var newHandle = () => this.handle(1, 2);

Modules

Modules should always be written using ES6 module syntax.

import React from 'react';

export default class MyComponent extends React.Component {}

Where possible, files should only export a single class using export default, although multiple functions can be exported from a single containing file.

Variable declaration

let replaces var, but binds to the current braces (typically indentation level). In most cases, let should be used, especially inside loops.

// Bad:
for ( var i = 0; i < 5; i++ ) {
	window.setTimeout( () => console.log( i ) );
}
//=> 5 5 5 5 5

// Good:
for ( let i = 0; i < 5; i++ ) {
	window.setTimeout( () => console.log( i ) );
}
//=> 0 1 2 3 4

Note that this applies to all blocks, not just those that typically create new scopes; in particular, a let variable will not travel upwards from an if:

// Bad:
if ( condition ) {
	let x = 1;
}
x; // undefined variable "x"

// Good:
if ( condition ) {
	var x = 1;
}

// Better:
let x;
if ( condition ) {
	x = 1;
}

Variable declarations should generally be just-in-time, typically when you initialise it to a value. If you cannot declare the variable when you initialise it, the declaration should be as close as practicable:

// Bad:
function () {
	let x;

	something();

	if ( condition ) {
		x = 1;
	}
}

// Good:
function () {
	something();

	let x;
	if ( condition ) {
		x = 1;
	}
}

Style

Trailing commas.

Multi-line array and object declarations should always have a trailing comma after each item. This cleans up the diff for future changes.

Single-line declarations do not need trailing commas.

Avoid Yoda conditions.

Yoda conditions are dumb and solve the wrong problem. You have my permission to not use Yoda conditions. - RM

Prefer functional programming over imperative.

Rather than using loops, use functional programming concepts like .map, .filter, etc.

Libraries

Avoid Underscore.

Most functionality in Underscore.js is available natively in the browser, and can be compiled by Webpack/Babel into ES5-compatible calls. Khan’s style guide includes a guide to replacing calls.

If you need Underscore functionality, consider using Lodash instead. Lodash allows importing singular functions, providing only the functionality you need:

import map from 'lodash/map';