Skip to content

Latest commit

 

History

History
924 lines (661 loc) · 21 KB

File metadata and controls

924 lines (661 loc) · 21 KB

LiveBy JavaScript Standard Style

Our styleguide is a weave based on JS Standard Style and Automattic's Calypso project.

Javascript Standard Style Calypso JS Style-Guide

Naming Conventions

Variable and function names should be full words, using camel case with a lowercase first letter. This also applies to abbreviations and acronyms.

// Good
var userIdToDelete, siteUrl;

// Bad
var userIDToDelete, siteURL;

Constructors intended for use with new should have a capital first letter (UpperCamelCase).

Names should be descriptive, but not excessively so. Exceptions are allowed for iterators, such as the use of i to represent the index in a loop.

Comments

Comments come before the code to which they refer, and should always be preceded by a blank line unless inserted as the first line in a block statement. Capitalize the first letter of the comment, and include a period at the end when writing full sentences. There must be a single space between the comment token (//) and the comment text.

Single line comments:

someStatement();

// Explanation of something complex on the next line
$( 'p' ).doSomething();

When adding documentation, use the jsdoc format.

/**
 * Represents a book.
 * @constructor
 * @param {string} title - The title of the book.
 * @param {string} author - The author of the book.
 */
function Book(title, author) {

}

Multi-line comments that are not a jsdoc comment should use //:

//
// This is a comment that is long enough to warrant being stretched
// over the span of multiple lines.

Inline comments are allowed as an exception when used to annotate special arguments in formal parameter lists:

function foo( types, selector, data, fn, /* INTERNAL */ one ) {
    // Do stuff
}

Type Checks

These are the preferred ways of checking the type of an object:

  • String: typeof object === 'string'
  • Number: typeof object === 'number'
  • Boolean: typeof object === 'boolean'
  • Object: typeof object === 'object'
  • null: object === null
  • undefined: object === undefined or for globals typeof window.someGlobal === 'undefined'

However, you don't generally have to know the type of an object. Prefer testing the object's existence and shape over its type.

Existence and Shape Checks

Prefer using the power of "truthy" in JavaScript boolean expressions to validate the existence and shape of an object to using typeof.

The following are all false in boolean expressions:

  • null
  • undefined
  • '' the empty string
  • 0 the number

But be careful, because these are all true:

  • '0' the string
  • [] the empty array
  • {} the empty object
  • -1 the number

To test the existence of an object (including arrays):

if ( object ) { ... }

To test if a property exists on an object, regardless of value, including undefined:

if ( 'desired' in object ) { ... }

To test if a property is present and has a truthy value:

if ( object.desired ) { ... }

To test if an object exists and has a property:

if ( object && 'desired' in object ) { ... }
if ( object && object.desired ) { ... }

Rules

  • Use 2 spaces for indentation.

    function hello (name) {
      console.log('hi', name)
    }
  • Use single quotes for strings except to avoid escaping.

    console.log('hello there')
    $("<div class='box'>")
  • No unused variables.

    function myFunction () {
      var result = something()   // ✗ avoid
    }
  • Add a space after keywords.

    if (condition) { ... }   // ✓ ok
    if(condition) { ... }    // ✗ avoid
  • *Add a space before a function declaration's parentheses.

    function name (arg) { ... }   // ✓ ok
    function name(arg) { ... }    // ✗ avoid
    
    run(function () { ... })      // ✓ ok
    run(function() { ... })       // ✗ avoid
  • Always use === instead of ==.
    Exception: obj == null is allowed to check for null || undefined.

    if (name === 'John')   // ✓ ok
    if (name == 'John')    // ✗ avoid
    if (name !== 'John')   // ✓ ok
    if (name != 'John')    // ✗ avoid
  • Infix operators must be spaced.

    // ✓ ok
    var x = 2;
    var message = 'hello, ' + name + '!';
    // ✗ avoid
    var x=2;
    var message = 'hello, '+name+'!';
  • Commas should have a space after them.

    // ✓ ok
    var list = [1, 2, 3, 4];
    function greet (name, options) { ... }
    // ✗ avoid
    var list = [1,2,3,4];
    function greet (name,options) { ... }
  • Keep else statements on the same line as their curly braces.

    // ✓ ok
    if (condition) {
      // ...
    } else {
      // ...
    }
    // ✗ avoid
    if (condition) {
      // ...
    }
    else {
      // ...
    }
  • For multi-line if statements, use curly braces.

    // ✓ ok
    if (options.quiet !== true) console.log('done')
    // ✓ ok
    if (options.quiet !== true) {
      console.log('done')
    }
    // ✗ avoid
    if (options.quiet !== true)
      console.log('done')
  • Always handle the err function parameter.

    // ✓ ok
    run(function (err) {
      if (err) throw err
      window.alert('done')
    })
    // ✗ avoid
    run(function (err) {
      window.alert('done')
    })
  • Always prefix browser globals with window..
    Exceptions are: document, console and navigator.

    window.alert('hi')   // ✓ ok
  • Multiple blank lines not allowed.

    // ✓ ok
    var value = 'hello world'
    console.log(value)
    // ✗ avoid
    var value = 'hello world'
    
    
    console.log(value)
  • For the ternary operator in a multi-line setting, place ? and : on their own lines.

    // ✓ ok
    var location = env.development ? 'localhost' : 'www.api.com'
    
    // ✓ ok
    var location = env.development
      ? 'localhost'
      : 'www.api.com'
    
    // ✗ avoid
    var location = env.development ?
      'localhost' :
      'www.api.com'
  • For var declarations, write each declaration in its own statement.

    // ✓ ok
    var silent = true
    var verbose = true
    
    // ✗ avoid
    var silent = true, verbose = true
    
    // ✗ avoid
    var silent = true,
        verbose = true
  • Wrap conditional assignments with additional parentheses. This makes it clear that the expression is intentionally an assignment (=) rather than a typo for equality (===).

    // ✓ ok
    while ((m = text.match(expr))) {
      // ...
    }
    
    // ✗ avoid
    while (m = text.match(expr)) {
      // ...
    }

Semicolons

  • No semicolons. (see: 1, 2, 3)

    window.alert('hi')   // ✓ ok
    window.alert('hi');  // ✗ avoid
  • Never start a line with ( or [. This is the only gotcha with omitting semicolons.

    ;(function () {
      window.alert('ok')
    }())

Semicolon - Helpful reading

And a helpful video:

All popular code minifiers in use today use AST-based minification, so they can handle semicolon-less JavaScript with no issues (since semicolons are not required in JavaScript).

In general, \n ends a statement unless:

  1. The statement has an unclosed paren, array literal, or object literal or ends in some other way that is not a valid way to end a statement. (For instance, ending with . or ,.)
  2. The line is -- or ++ (in which case it will decrement/increment the next token.)
  3. It is a for(), while(), do, if(), or else, and there is no {
  4. The next line starts with [, (, +, *, /, -, ,, ., or some other binary operator that can only be found between two tokens in a single expression.

The first is pretty obvious. Even JSLint is ok with \n chars in JSON and parenthesized constructs, and with var statements that span multiple lines ending in ,.

The second is super weird. I’ve never seen a case (outside of these sorts of conversations) where you’d want to do write i\n++\nj, but, point of fact, that’s parsed as i; ++j, not i++; j.

The third is well understood, if generally despised. if (x)\ny() is equivalent to if (x) { y() }. The construct doesn’t end until it reaches either a block, or a statement.

; is a valid JavaScript statement, so if(x); is equivalent to if(x){} or, “If x, do nothing.” This is more commonly applied to loops where the loop check also is the update function. Unusual, but not unheard of.

The fourth is generally the fud-inducing “oh noes, you need semicolons!” case. But, as it turns out, it’s quite easy to prefix those lines with semicolons if you don’t mean them to be continuations of the previous line. For example, instead of this:

foo();
[1,2,3].forEach(bar);

you could do this:

foo()
;[1,2,3].forEach(bar)

The advantage is that the prefixes are easier to notice, once you are accustomed to never seeing lines starting with ( or [ without semis.

End quote from "An Open Letter to JavaScript Leaders Regarding Semicolons".

Semicolon - Tips

Avoid clever short-hands

Clever short-hands are discouraged, in favor of clear and readable expressions, whenever possible. So, while this is allowed:

;[1, 2, 3].forEach(bar)

This is much preferred:

var nums = [1, 2, 3]
nums.forEach(bar)

Additional Best Practices

Iteration

The functional programming inspired methods that were added in ECMA5 improve readability. Use them throughout.

var posts = postList.map( function( post ) {
	return <Post post={ post } key={ post.global_ID } />;
} );

When iterating over a large collection using a for loop, it is recommended to store the loop’s max value as a variable rather than re-computing the maximum every time:

Objects

Object declarations can be made on a single line if they are short (remember the line length guidelines). When an object declaration is too long to fit on one line, there must be one property per line. Property names only need to be quoted if they are reserved words or contain special characters:

// Preferred
var map = {
    ready: 9,
    when: 4,
    'you are': 15
};

// Acceptable for small objects
var map = { ready: 9, when: 4, 'you are': 15 };

// Bad
var map = { ready: 9,
    when: 4, 'you are': 15 };

Arrays and Function Calls

Always include extra spaces around elements and arguments:

array = [ a, b ];

foo( arg );

foo( 'string', object );

foo( options, object[ property ] );

foo( node, 'property', 2 );

Best Practices

Variable names

The first word of a variable name should be a noun or adjective (not a verb) to avoid confusion with functions.

You can prefix a variable with verb only for boolean values when it makes code easier to read.

// bad
const play = false;

// good
const name = 'John';
const blueCar = new Car( 'blue' );
const shouldFlop = true;

Function names

The first word of a function name should be a verb (not a noun) to avoid confusion with variables.

// bad
function name() {
    return 'John';
}

// good
function getName() {
    return 'John';
}

// good
function isValid() {
    return true;
}

Arrays

Creating arrays in JavaScript should be done using the shorthand [] constructor rather than the new Array() notation.

var myArray = [];

You can initialize an array during construction:

var myArray = [ 1, 'WordPress', 2, 'Blog' ];

In JavaScript, associative arrays are defined as objects.

Objects

There are many ways to create objects in JavaScript. Object literal notation, {}, is both the most performant, and also the easiest to read.

var myObj = {};

Object literal notation should be used unless the object requires a specific prototype, in which case the object should be created by calling a constructor function with new.

var myObj = new ConstructorMethod();

Object properties should be accessed via dot notation, unless the key is a variable, a reserved word, or a string that would not be a valid identifier:

prop = object.propertyName;
prop = object[ variableKey ];
prop = object['default'];
prop = object['key-with-hyphens'];

ES6

We support and encourage ES6 features thanks to Babel transpilation and the accompanying polyfill. There are still a couple of minor caveats to be aware of regarding Classes.

Let and Const

You can use const for all of your references.

Why? This ensures that you can't reassign your references (mutation), which can lead to bugs and difficult to comprehend code.

// good
var a = 1,
    b = 2;

// better
const a = 1,
    b = 2;

If you must mutate references, you can use let. Otherwise const is preferred.

Why? let is block-scoped rather than function-scoped like var.

// good
var count = 1;
if ( true ) {
    count += 1;
}

// better
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

More about:

Arrow Functions

When you must use function expressions (as when passing an anonymous function), you can use arrow function notation.

Why? It creates a version of the function that executes in the context of this, which is usually what you want, and is a more concise syntax.

Why not? If you have a fairly complicated function, you might move that logic out into its own function declaration.

// good
function Person() {
    this.age = 0;

    setInterval( function() {
        this.age++;
    }.bind( this ), 1000 );
}
let p = new Person();

// better
function Person() {
    this.age = 0;

    setInterval( () => {
        this.age++;
    }, 1000 );
}
let p = new Person();

If the function body fits on one line and there is only a single argument, feel free to omit the braces and parentheses, and use the implicit return. Otherwise, add the parentheses, braces, and use a return statement.

Why? Syntactic sugar. It reads well when multiple functions are chained together.

Why not? If you plan on returning an object.

// bad
// it's too magic, it works only with parentheses, otherwise it returns collection of undefine values
[ 1, 2, 3 ].map( x => ( { value: x * x } ) );

// good
[ 1, 2, 3 ].map( x => x * x );

// good
[ 1, 2, 3 ].reduce( ( total, n ) => {
    return total + n;
}, 0 );

More about:

Object Property Shorthand

You can use property value shorthand.

Why? It is shorter to write and descriptive.

const lukeSkywalker = 'Luke Skywalker';

// good
const obj = {
    lukeSkywalker: lukeSkywalker,
};

// better
const obj = {
    lukeSkywalker,
};

Group your shorthand properties at the beginning of your object declaration.

Why? It's easier to tell which properties are using the shorthand.

const anakinSkywalker = 'Anakin Skywalker',
    lukeSkywalker = 'Luke Skywalker';

// bad
const obj = {
    episodeOne: 1,
    twoJediWalkIntoACantina: 2,
    lukeSkywalker,
    episodeThree: 3,
    mayTheFourth: 4,
    anakinSkywalker,
};

// good
const obj = {
    lukeSkywalker,
    anakinSkywalker,
    episodeOne: 1,
    twoJediWalkIntoACantina: 2,
    episodeThree: 3,
    mayTheFourth: 4,
};

More about:

Object Method Shorthand

You can use object method shorthand.

// good
const atom = {
    value: 1,

    addValue: function ( value ) {
        return atom.value + value;
    },
};

// better
const atom = {
    value: 1,

    addValue( value ) {
        return atom.value + value;
    },
};

More about:

Object Computed Properties

You can use computed property names when creating objects with dynamic property names.

Why? They allow you to define all the properties of an object in one place.

function getKey( k ) {
    return `a key named ${k}`;
}

// good
const obj = {
    id: 5,
    name: 'San Francisco',
};
obj[ getKey( 'enabled' ) ] = true;

// better
const obj = {
    id: 5,
    name: 'San Francisco',
    [ getKey( 'enabled' ) ]: true,
};

More about:

Template Strings

When programmatically building up strings, you can use template strings instead of concatenation.

Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features.

// good
function sayHi( name ) {
    return 'How are you, ' + name + '?';
}

// better
function sayHi( name ) {
    return `How are you, ${ name }?`;
}

More about:

Destructuring

You can use object destructuring when accessing and using multiple properties of an object.

Why? Destructuring saves you from creating temporary references for those properties.

// good
function getFullName( user ) {
    const firstName = user.firstName,
        lastName = user.lastName;

    return `${ firstName } ${ lastName }`;
}

// better
function getFullName( user ) {
    const { firstName, lastName } = user;

    return `${ firstName } ${ lastName }`;
}

// best
function getFullName( { firstName, lastName } ) {
    return `${ firstName } ${ lastName }`;
}

You can use array destructuring.

const arr = [ 1, 2, 3, 4 ];

// good
const first = arr[0],
    second = arr[1];

// better
const [ first, second ] = arr;

Use object destructuring for multiple return values, not array destructuring.

Why? You can add new properties over time or change the order of things without breaking call sites.

function processInput( input ) {
    let left, right, top, bottom;
    // some assignment happens
    return { left, right, top, bottom };
}
const { left, top } = processInput( input );

More about:

Default Parameters

You can use default parameter syntax rather than mutating function arguments.

// really bad
function handleThings( opts ) {
    // No! We shouldn't mutate function arguments.
    // Double bad: if opts is falsy it'll be set to an object which may
    // be what you want but it can introduce subtle bugs.
    opts = opts || {};
    // ...
}

// good
function handleThings( opts ) {
    if ( opts === void 0 ) {
        opts = {};
    }
    // ...
}

// better
function handleThings( opts = {} ) {
    // ...
}

More about:

Rest

Never use arguments, opt to use rest syntax ... instead.

Why? ... is explicit about which arguments you want pulled. Plus rest arguments are a real Array and not Array-like like arguments.

// bad
function concatenateAll() {
    const args = Array.prototype.slice.call( arguments );

    return args.join( '' );
}

// good
function concatenateAll( ...args ) {
    return args.join( '' );
}

More about:

Array Spreads

You can use array spreads ... to copy arrays.

// good
const itemsCopy = items.slice();

// better
const itemsCopy = [ ...items ];

More about: