Skip to content
This repository has been archived by the owner on Aug 3, 2023. It is now read-only.

Latest commit

 

History

History
1486 lines (1057 loc) · 28.9 KB

javascript.md

File metadata and controls

1486 lines (1057 loc) · 28.9 KB

JavaScript Code Style

Table of Contents

General

  • Files should be encoded in UTF-8 without BOM.
  • The recommended line-break character is LF - \n.
  • Files should end with a LF character.
  • One level of indentation is achieved with 4 space characters.
  • Lines should be no longer than 120 characters.
  • Trailing whitespace at the end of lines should be removed.

↑ back to TOC

Naming

  • variableNamesLikeThis
  • functionNamesLikeThis
  • functionArgumentsLikeThis
  • ClassNamesLikeThis
  • EnumNamesLikeThis
  • methodNamesLikeThis
  • CONSTANTS_LIKE_THIS
  • namespacesLikeThis
  • private or protected properties and methods should be prefixed with a single _ character
  • Shortened and abbreviated names should be avoided.
  • Common abbreviations, such as JSON and XML are written in CamelCase. For example: Json, Xml.

↑ back to TOC

Variable declaration

  • Each variable should be declared:

    • using a var statement;
    • only once in the current scope;
    • on a new line;
    • as close as possible to the place where it's first used.
  • Each var statement should have only one variable declared in it.

Good:

var keys = ['foo', 'bar'];
var values = [23, 42];

var object = {};
while (items.length) {
    var key = keys.pop();
    object[key] = values.pop();
}

Bad:

var keys = ['foo', 'bar'],
    values = [23, 42],
    object = {},
    key;

while (items.length) {
    key = keys.pop();
    object[key] = values.pop();
}

↑ back to TOC

Literals

Objects

  • There should be no whitespace after the opening and before the closing curly braces:

    var obj = {a: 1, b: 2, c: 3};
    
    this.method({a: 1, b: 2});
  • There should be no whitespace characters before the colon:

    var obj = {
        prop: 0
    };
  • Only property names should be aligned within object literals:

    Good:

    var obj = {
        a: 0,
        b: 1,
        lengthyName: 2
    };

    Bad:

    var obj = {
        a          : 0,
        b          : 1,
        lengthyName: 2
    };
  • Quotes around property names should be typed only if needed:

    Good:

    var obj = {
        key: 0,
        'key-key': 1
    };

    Bad:

    var obj = {
        'key': 0,
        'key-key': 1
    };
  • Spaces inside computed property brackets should not be used:

    Good:

    var value = obj[key];

    Bad:

    var value = obj[ key ];

↑ back to TOC

Arrays

  • When enumerating elements in an array literal, spaces should be typed after the comma only:

    var fellowship = ['foo', 'bar', 'baz'];

↑ back to TOC

Strings

  • String literals should use single quotes:

    var lyrics = 'Never gonna give you up. Never gonna let you down. Never gonna turn around and desert you.';
  • If a string contains a single quote character, it should be escaped:

    var test = 'It shouldn\'t fail';

↑ back to TOC

Semicolons

Statements should always end with a semicolon.

↑ back to TOC

Keywords

  • Keywords are always followed by a single space character:

    if (test) {
        // ...
    }
    
    function foo() {
        // ...
    }
    
    var bar = function () {
        // ...
    };
  • If the keyword is followed by a semicolon, there should be no space between them:

    return;

↑ back to TOC

Block statements

  • The opening curly brace should be on the same line and separated with one space character:

    if (test) {
        // ...
    }
    
    function foo() {
        // ...
    }
  • Branching and looping statements should always be surrounded with curly braces:

    Good:

    if (test) {
        return;
    }

    Bad:

    if (test)
        return;
    
    if (test) return;
    
    if (test) { return; }

↑ back to TOC

Conditional statements

if

  • The else keyword should be on the same line as the closing brace of the if-part of the statement:

    if (test) {
        // ...
    } else {
        // ...
    }
  • Condition statements should not contain assignment operations:

    Good:

    var foo = bar();
    if (foo > 0) {
        // ...
    }

    Bad:

    var foo;
    if ((foo = bar()) > 0) {
        // ...
    }
  • Logical operators should not be used for conditional branching:

    Good:

    if (condition) {
        actionIfTrue();
    } else {
        actionIfFalse();
    }

    Bad:

    condition && actionIfTrue() || actionIfFalse();
  • Conditions longer than the maximum line length should be divided as in the example:

    if (longCondition ||
        anotherLongCondition &&
        yetAnotherLongCondition
    ) {
        // ...
    }
  • Yoda conditions should not be used:

    Good:

    if (getType() === 'driving') {
    
    }

    Bad:

    if ('driving' === getType()) {
    
    }

↑ back to TOC

switch

The switch statement should be written as in the example:

switch (value) {
    case 1:
        // ...
        break;

    case 2:
        // ...
        break;

    default:
        // ...
        // no break keyword on the last case
}

↑ back to TOC

Loop statements

for

  • Expressions, enclosed in round parentheses, should be separated by space after semicolon:

    Good:

    for (var i = 0; i < 10; i++) {
        doSomething();
    }

    Bad:

    for (var i = 0 ; i < 10 ; i++) {
        doSomething();
    }
  • Complicated expressions in round parentheses should be avoided.

↑ back to TOC

do...while

  • while keyword should be on the same line as the closing brace (like in conditional statements):

    do {
        statements;
    } while (condition);

↑ back to TOC

Operators

'with' operator

The with operator should not be used.

↑ back to TOC

Comparison operators

If there is no need for type casting, the strict equality operator === (or strict inequality !==) should be used.

↑ back to TOC

Ternary operator

The ternary operator should be written as in the examples:

var x = a ? b : c;

var y = a ?
    longButSimpleOperandB : longButSimpleOperandC;

var z = a ?
    moreComplicatedB :
    moreComplicatedC;

↑ back to TOC

Unary operators

Unary operators should be typed without whitespace between them and their operands:

var foo = !bar;

Exceptions from this rule are the unary special JS operators).

↑ back to TOC

eval

The eval function should be avoided. json serialized data should be parsed with JSON.parse.

↑ back to TOC

undefined

  • Checking for undefined value of declared variable (e.g. function argument) should be done by using the strict equality operator:

    Explanation:

    • In modern browsers (IE9+, Opera 12.16+, Firefox 4+) undefined is immutable (a non-configurable, non-writable property of the global object).
    • It prevents undeclared variables usage.

    Good:

    x === undefined

    Bad:

    typeof x === 'undefined'
    x === void 0

    Exceptions:

    • typeof should be used if you need to support old browsers (like IE8) where window.undefined property is mutable.

    • typeof may be used in place where string type is expected:

      switch (typeof x) {
          case 'number':
              // ...
          case 'undefined':
              // ...
      }
  • Checking for existence of a global variable should be done by using typeof operator or by checking existence of a property of the global object:

    Explanation: An attempt to access to undeclared variable will result an error.

    if (typeof Foo !== 'undefined') {
        // ...
    }
    
    // Also okay for browser only code (`window` is unavailable in Node.js)
    if (window.Foo !== undefined) {
        // ...
    }

↑ back to TOC

Parentheses

  • Should not be used with the unary operators delete, typeof and void, or with the keywords return, throw and new:

    Good:

    delete obj.key;
    typeof x === 'number';
    new Type();
    throw new Error();

    Bad:

    delete(obj.key);
    typeof(x) === 'number';
    new(Type)();
    throw(new Error());
  • Explicit parentheses in logical or mathematical expressions can be used to increase readability:

    ((a - b > c) && c) || (c + d && d + 1) || e; // equivalent to a - b > c && c || c + d && d + 1 || e

↑ back to TOC

Exceptions

throw should be used with new Error or an object of a class derived from Error:

Good:

throw new Error('msg');

Bad:

throw 'msg';

↑ back to TOC

Type casting

Type casting should be done explicitly:

Good:

Boolean(foo)
Number(bar)
String(baz)
[].indexOf(qux) === -1 or [].indexOf(qux) < 0

Bad:

!!foo
+bar
baz + ''
~[].indexOf(qux)

↑ back to TOC

Multi-line statements

  • If a statement is longer than the maximum line length, it is split into several lines and properly indented.

  • Lines of the statement should be split after an operator:

    var debt = this.calculateBaseDebt() + this.calculateSharedDebt() + this.calculateDebtPayments() +
        this.calculateDebtFine();
  • Closing parentheses should be on a new line with the indentation of the current block statement:

    Good:

    DoSomethingThatRequiresALongFunctionName(
        veryLongArgument1,
        argument2,
        argument3,
        argument4
    );
    anotherStatement;

    Bad:

    DoSomethingThatRequiresALongFunctionName(
        veryLongArgument1,
        argument2,
        argument3,
        argument4);
    anotherStatement;

↑ back to TOC

Method chaining

When a method is called on a new line, it should:

  • Be one indentation level deeper than the target object.
  • Begin with the property access operator ..

Good:

someObject
    .operation()
    .operationWithCallback(function (obj) {
        obj.processed = true;
    })
   .end();

Bad:

someObject.
   start().
   end();

someObject
.start()
.end();

↑ back to TOC

String concatenation

  • Strings should be concatenated with the + operator.
  • The [].join('') should be avoided.
  • Escaping newline literals inside strings should be avoided.

Good:

var foo = 'A rather long string of English text, an error message ' +
    'actually that just keeps going and going -- an error ' +
    'message to make the Energizer bunny blush (right through ' +
    'those Schwarzenegger shades)! Where was I? Oh yes, ' +
    'you\'ve got an error and all the extraneous whitespace is ' +
    'just gravy.  Have a nice day.';

Bad:

var foo = 'A rather long string of English text, an error message \
          actually that just keeps going and going -- an error \
          message to make the Energizer bunny blush (right through \
          those Schwarzenegger shades)! Where was I? Oh yes, \
          you\'ve got an error and all the extraneous whitespace is \
          just gravy.  Have a nice day.';

↑ back to TOC

Empty lines

A single empty line can be used as a separator for grouping the code into logical blocks:

doSomethingTo(x);
doSomethingElseTo(x);
andThen(x);

nowDoSomethingWith(y);

andNowWith(z);

↑ back to TOC

Comments

  • In-line comments should start with //. Between the // and the text of the comment should be one space character.
  • Comments for functions, classes, etc. should be written according to the jsdoc documentation syntax.

↑ back to TOC

Functions

this

  • Binding the this value for function calls should be done using Function.prototype.bind:

    doAsync(function () {
        this.fn();
    }.bind(this));

    Note: in ECMAScript 6 arrow functions are preferred.

  • Preferably, the argument for this value should be used (if available):

    Good:

    [1, 2, 3].forEach(function (n) {
        this.fn(n);
    }, this);

    Bad:

    [1, 2, 3].forEach(function (n) {
        this.fn(n);
    }.bind(this));
  • If assigning the this value to a variable, the variable should be named _this:

    var _this = this;
    doAsync(function () {
        _this.fn();
    });

↑ back to TOC

Return statement

Assignment in return statement should be avoided:

Good:

var lazyCompute = (function () {
    var result;
    return function () {
        if (!result) {
            result = compute();
        }
        return result;
    }
}());

Bad:

var lazyCompute = (function () {
    var result;
    return function () {
        return result || (result = compute());
    }
}());

↑ back to TOC

Classes

  • "Symmetrical" methods should be declared one after the other. For example:

    function Foo() {}
    
    // Destructors are placed right after the constructor.
    Foo.prototype.destruct = function () {};
    
    Foo.prototype.someMethod = function () {};
  • Constructor should not be used as a factory function:

    Explanation:

    • It makes code explicit.
    • It simplifies migration to ES6 classes, because class constructors cannot be invoked without new.

    Bad:

    function Foo(bar) {
        if (!(this instanceof Foo)) {
            return new Foo(bar);
        }
        // ...
    }
    
    var foo = Foo();

↑ back to TOC

Enums

  • Enum names should be in UpperCamelCase.
  • Enum members should be in ALL_CAPS.
var Color = {
    BLUE: '#0000dd',
    RED: '#dd0000'
};

↑ back to TOC

ECMAScript 2015

This section describes code style for ECMAScript 2015 Language Specification.

Strict mode

  • Strict mode should be used.

    Explanation:

    • It prevents nasty bugs.
    • Many useful features of language (e.g. classes, let declaration, block scopes) are available only in strict mode.
    • It simplifies migration to ES6 modules, because they are executed in strict mode.
  • Strict mode should be enabled explicity using the 'use strict' pragma.

    Explanation:

    • Dependencies of your code may not work in strict mode.
    • Your code can be used in non-strict mode.

↑ back to TOC

Variable declaration

  • Avoid using var.

  • All immutable references should be declared using a const.

  • let should be used only for mutable references (i.e. when variable will be (re)assigned different value later in the code).

    Good:

    const count = observers.length;
    let index = 0;
    while (index < count) {
        const observer = observers[index];
        observer(...args);
        index = index + 1;
    }

    Bad:

    // Reader expects count to change!
    let count = observers.length;
    let index = 0;
    while (index < count) {
        const observer = observers[index];
        observer(...args);
        index = index + 1;
    }
    
    const count = observers.length;
    let index = 0;
    while (index < count) {
        // Reader expects observer to change within the block!
        let observer = observers[index];
        observer(...args);
        index = index + 1;
    }
    
    const count = observers.length;
    // Do not use `var`
    var index = 0;
    while (index < count) {
        observers[index](...args);
        index = index + 1;
    }
  • If the reference is immutable, but the value is mutable, const decalaration should be used:

    Good:

    const query = {};
    query.param = 'value';

    Bad:

    let query = {};
    query.param = 'value';

↑ back to TOC

Classes

  • For class definition the class keyword should be used:

    Good:

    class Circle {
        constructor(x, y, radius) {
            this.x = x;
            this.y = y;
            this.radius = radius;
        }
    
        area() {
            return Math.PI * this.radius * this.radius;
        }
    }

    Bad:

    function Circle(x, y, radius) {
        this.x = x;
        this.y = y;
        this.radius = radius;
    }
    
    Circle.prototype.area = function () {
        return Math.PI * this.radius * this.radius;
    };
  • There should be one whitespace after the class name:

    Good:

    class Circle {}

    Bad:

    class Circle{}
  • There should be no whitespace after method name:

    Good:

    class Circle {
        area() {}
    }

    Bad:

    class Circle {
        area () {}
    }
  • There should be one whitespace before the opening curly brace of method's body:

    Bad:

    class Circle {
        area(){}
    }
  • The constructor (if exists) should be the first method in a class definition:

    Good:

    class Circle {
        constructor() {}
    
        area() {}
    }

    Bad:

    class Circle {
        area() {}
    
        constructor() {}
    }
  • For inheritance the extends keyword should be used:

    Good:

    class Stream extends EventEmitter {}

    Bad:

    var util = require('util');
    
    class Stream() {
        constructor() {
            EventEmitter.call(this);
        }
    }
    
    util.inherits(Stream, EventEmitter);

↑ back to TOC

Arrow functions

  • Arrow function should be used where an anonymous function is expected (when you need function and you don't want bind it to an identifier):

    Explanation: Arrow functions capture the this value of the enclosing context. That prevents run-time errors with unexpected values of this. Also it's a more efficient method than binding this value using Function.prototype.bind.

    [1, 2, 3].map((x) => {
        // ...
    });
    
    // Use `function` here, because pass `someObj` as `this` argument.
    [1, 2, 3].map(function (x) {
        // ...
    }, someObj);
    
    // Use `function` here, because specify name.
    decorate(function createSomething() {
        // ...
    });
  • Always add parentheses around arrow function parameters:

    Explanation:

    • This style is consistent with cases when function takes zero or more than one parameters.
    • You need to alter the code less, if the number of parameters changes.

    Good:

    [1, 2, 3].map((x) => x * 2);
    [1, 2, 3].reduce((acc, n) => acc + n);

    Bad:

    [1, 2, 3].map(x => x * 2);
  • Before and after an arrow function's arrow (=>) whitespace is required:

    [1, 2, 3].map((x) => x * 2);
  • If the function body consists of a single expression, braces should be omitted:

    Good:

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

    Bad:

    [1, 2, 3].map((x) => { return x * 2; });

↑ back to TOC

Template strings

  • Spaces in placeholders (after ${ and before }) should not be used:

    Good:

    `Hello ${name}`

    Bad:

    `Hello ${ name }`

↑ back to TOC

Default parameters

  • Spaces around the = sign should be used:

    Good:

    function project(point, zoom = 23) {}

    Bad:

    function project(point, zoom=23) {}

↑ back to TOC

Destructuring assignment

  • In destructuring assignment rules for Objects and Arrays should be used:

    Good:

    const [a, b] = someArray;
    const {a, b} = someObject;

    Bad:

    const [ a, b ] = someArray;
    const { a, b } = someObject;
  • If some value in array destructuring is ignored, it should not be marked with space:

    Good:

    const [first,, third] = ['foo', 'bar', 'baz'];
    const [,, third] = ['foo', 'bar', 'baz'];

    Bad:

    const [first, , third] = ['foo', 'bar', 'baz'];
    const [ , , third] = ['foo', 'bar', 'baz'];
  • For default values spaces around = should be used:

    Good:

    function sendRequest({cache = true, timeout = 200}) {}

    Bad:

    function sendRequest({cache=true, timeout=200}) {}
  • Long destructuring patterns should be written as in the example:

    const {
        protocol,
        hostname,
        port,
        pathname,
        query
    } = url.parse(urlString);
    
    function formatUrl({
        protocol,
        hostname,
        port,
        pathname,
        query
    }) {
        // ...
    }

↑ back to TOC

Rest parameters and spread operator

↑ back to TOC

Generators

  • The asterisk * in a generator declaration should be sticked to the function keyword:

    Good:

    function* createIterator() {
        yield 1;
    }
    
    const createIterator = function* () {
        yield 1;
    };

    Bad:

    function *createIterator() {
        yield 1;
    }
    
    const createIterator = function * () {
        yield 1;
    };
  • In a shorthand method the asterisk should be sticked to the method name:

    Good:

    class Graph {
        *edges() {}
    }

    Bad:

    class Graph {
        * edges() {}
    }
  • In an yield* expression the asterisk should be sticked to the yield keyword:

    Good:

    function* gen() {
        yield* anotherGen();
    }

    Bad:

    function* gen() {
        yield *anotherGen();
    }

↑ back to TOC

Modules

  • There should be no whitespace after the opening and before the closing curly braces (like in objects):

    Good:

    import {name1, name2} from 'foo';

    Bad:

    import { name1, name2 } from 'foo';
  • Long named imports and exports should be written as in the examples:

    import {
        name1 as localName1,
        name2,
        name3,
        name4
    } from 'src/foo';
    
    export {
        name1 as exportName1,
        name2,
        name3,
        name4
    };

↑ back to TOC

ECMAScript 2017

async-await

  • Avoid unnecessary return await.

    Explanation: An async function always wraps the return value in a Promise. Using return await just adds extra time before the resulting promise is resolved without changing the semantics.

    Good:

    async function foo() {
        return bar();
    }

    Bad:

    async function foo() {
        return await bar();
    }
  • Don't use async when it has no useful effect.

    It's easy to get in the habit of using async on any function that does anything related to asynchrony. But in some cases, it's extraneous. If you can omit the async without changing the behavior of the function, do so.

    Good:

    function afterTwoThings(first, second) {
        return Promise.all([first, second]);
    }

    Bad:

    async function afterTwoThings(first, second) {
        return Promise.all([first, second]);
    }

    Cases where async is useful include:

    • You are using await. (This is the obvious one.)
    • You are returning an error asynchronously.
    • You are returning a value and you want it implicitly wrapped in a promise.

    Good:

    async function asyncError() {
        throw new Error('Error!');
    }
    
    const asyncValue = async () => 'value';

↑ back to TOC

Node.js

Importing modules

  • Modules should be imported in the beginning of the file, after the description of the module (if present):

    Good:

    var http = require('http');
    var fs = require('fs');
    
    // code here

    Bad:

    var http = require('http');
    
    // code here
    
    var fs = require('fs');
    
    // code here

    Exception: This rule does not apply to modules that are imported "on demand".

  • Module import calls should be grouped according to the following order:

    1. Standard node.js modules (i.e. fs, util, etc.).
    2. External lib modules.
    3. Modules of the current application.

↑ back to TOC

Licence

CC BY-SA 4.0

↑ back to TOC