Skip to content

Latest commit

 

History

History
executable file
·
1343 lines (980 loc) · 21.8 KB

javascript.md

File metadata and controls

executable file
·
1343 lines (980 loc) · 21.8 KB

JavaScript Style Guide

Table Of Contents

Conventions

Grammar

Constructors

Objects

Strings

Arrays

Functions

Enumerables

ES2015 and ES2016

  • ES2015 and ES2016 syntax MUST be employed whenever possible

Line Endings

  • Use *nix line endings

Type Coercion

  • MUST be explicit
  • MUST use coercion methods available on types over global methods
// good
( 123 ).toString();

Date().toString();

let value = Number( '3.14' );

return Boolean( result );


// bad
String( 123 );

String( Date() );

let value = + "3.14";

return !!result;

Block Statements

  • MUST use spaces before leading brace
// good
function doStuff( foo ) {
    return foo;
};

// bad
function doStuff( foo ){
    return foo;
};
  • SHOULD use spaces after leading parenthesis
  • SHOULD use spaces before closing parenthesis when part of block statment on a single line
  • SHOULD NOT use spaces before closing parenthesis when part of a series of closing parenthesis or brackets on their own line
// should use leading space
// should use closing space
return fooBar( foo );

// should use leading space
// should not use closing space
return fooBar( foo, ( bars ) => {
    return bars.map( ( bar ) => {
        return bar * 2;
    });
});
  • Opening curly brace ({) MUST be on the same line as the beginning of a statement or declaration
// good
return fooBar( foo, ( bars ) => {
    return bars.map( ( bar ) => {
        return bar * 2;
    });
});

//bad
return fooBar( foo, (bars) =>
{
    return bars.map((bar) =>
    {
        return bar * 2;
    });
});
  • MUST keep else and its accompanying braces on the same line
let bar;

// good
if ( 1 === foo ) {
    bar = 2;
} else {
    bar = '2';
}

// bad
if ( 1 === foo ) {
    bar = 2;
}
else if ( 2 === foo ) {
    bar = 1;
}
else {
    bar = 3;
}
  • MUST use multiline format
// good
while ( foo > 3 ) {
    ...
}

// bad
while ( foo > 3 ) { ... }

Conditional Statements

  • MUST NOT contain value assignments
// bad
if ( someVariable = 'someString' ) {
    ...
}
  • MUST use strict equality (=== and !==)

  • When using strict equality you MUST place the value on the left of the operation and the item you are checking on the right

// good
if ( false === foo ) {
    ...
}

// bad
if ( foo === false ) {
    ...
}
  • Non-strict equality comparisons SHOULD be constructed in a "grammar-friendly" order
// recommended
if ( itemCount > 0 ) {
    ...
}

// allowed
if ( 0 < itemCount ) {
    ...
}
  • MUST use curly braces for all conditional blocks
// good
if ( notFound ) {
    doBarrelRoll();
} else {
    jumpOnCouch();
}

// bad
if ( notFound )
    doBarrelRoll();
else
    jumpOnCouch();
  • MUST use explicit conditions when checking for non null, undefined, true, false values
const foo = 'foo';
const arr = [ 1, 2, 3 ];

// good
if ( arr.length > 0 ) {
    return;
}

if ( '' !== foo ) {
    ...
}

// bad
if ( !arr.length ) {
    return;
}

if ( !foo ) {
    return;
}
  • MUST use multiline format
// good
if ( 'bar' === foo ) {
    return;
}

// bad
if ( 'bar' === foo ) { return; }

No-op conditionals

  • MUST be placed as close to the beginning of the logic block as possible, including before lets or consts
// good
sortColumn( column ) {
    if ( this.loading ) {
        return;
    }

    let columnTitle = column.title;

    if ( !columnTitle ) {
        return;
    }

    let firstName = 'Joe';
    let lastName = 'Smith';
}

// bad
sortColumn( column ) {
    let columnTitle = column.title;
    let firstName = 'Joe';
    let lastName = 'Smith';

    if ( this.loading ) {
        return;
    }

    if ( !columnTitle ) {
        return;
    }
}

Commas

  • MUST NOT use trailing commas
// good
const foo = {
    bar: [ 1, 2, 3 ],
    baz: {
        a: 'a'
    }
}

// bad
const foo = {
    bar: [1, 2, 3],
    baz: {
        a: 'a',
    },
}
  • MUST NOT use leading commas
// good
const potato = [
    'potatoes',
    'are',
    'delicious'
];

// bad
const potato = [
    'potatoes'
    , 'are'
    , 'delicious'
];

Semicolons

  • Use semicolons;

Comments

  • JSDoc conventions MUST be used at all times

  • There are two types of descriptions:

    • short, usually consisting of a single line. SHOULD almost always be populated and MUST placed on the first line of a DocBlock when it is
    • long, consisting of multiple lines of text. Is OPTIONAL and if populated it MUST be placed after the short description
  • There MUST always be a single, empty line after each of the description types

  • There MUST always be a single, empty line after the end of each @example block

  • You MUST NOT use synonyms (e.g. use @returns instead of @return)

  • @type and @param types MUST be capitalized (e.g. Number, Boolean, Object, Function, ...)

    • The exception to this is undefined and null, both which MUST remain lowercased
  • When a function does not return a value it MUST be documented as returning undefined

@returns {undefined}
  • Characters SHOULD NOT be spaced out for evenness
/**
 * Bad spacing example
 *
 * @param   {Number}  age        Age of the registrant
 * @param   {String}  fullName   Fullname of the registrant
 * @param   {Boolean} registered Whether registrant is registered
 * @returns {undefined}
 */
function( age, fullName, registered ) { ... }

/**
 * Good spacing example
 *
 * @param {Number} age Age of the registrant
 * @param {String} fullName Fullname of the registrant
 * @param {Boolean} registered Whether registrant is registered
 * @returns {undefined}
 */
function( age, fullName, registered ) { ... }
  • When specifying a value with the @augments tag...

    • ...classes referenced from external libraries MUST be referenced in the format of <library>/<pathing>/<class>
    • ...classes referenced from within the same project MUST be referenced beginning with module: and then the path to the class
  • Optional parameters MUST be denoted via the use of brackets ( [ ] ) around the parameter name and not an equal sign (=) after the type value

  • @typedef blocks SHOULD be positioned before the DocBlocks that use them

  • MUST use // for non-documenting comments (both single and multiline)

// good
function foo() {
  const bar = 5;

  // multiplies `bar` by 2.
  const newBar = fooBar( bar );

  console.log( newBar );
}

// bad
function foo() {
  const bar = 5;

  /* multiplies `bar` by 2. */
  const newBar = fooBar( bar );

  console.log( newBar );
}

Variables

  • MUST NOT ever use var.
  • MUST use const to declare variables with a constant reference (not value)
  • MUST use let to declare variables with a variable reference
  • MUST use const over let whenever possible
// good
const a = [ 1, 2, 3 ];
let b = [ 4, 5, 6 ];

function doStuff() {
    b = [ 1, 2, 3 ];

    return b;
}

// bad
var a = [1, 2, 3];
let b = [1, 2, 3];
  • Note that const refers to a constant reference, not a constant value
const coolKids = [ 'Estelle', 'Lauren', 'Romina' ];
coolKids.push( 'Marin' );
console.log( coolKids ); // ['Estelle', 'Lauren', 'Romina', 'Marin']

coolKids = [ 'Doug', 'Lin', 'Dan' ]; // SyntaxError: "coolKids" is read-only
  • Note that both let and const are block scoped
{
    let a = 1;
    const b = 2;
}
console.log( a ); // ReferenceError
console.log( b ); // ReferenceError
  • consts SHOULD be defined inline where they are needed and don't need to be placed at the beginning of the scoped block
// good
const isTrue = true;

if ( foo ) {
    const bar = 123;
}

const baz = 456;


// bad
const bar = 123;
const isTrue = true;
if ( foo ) { ... }
  • MUST use a single const declaration for each assignment
// good
const a = 1;
const b = 2;

// bad
const a = 1, b = 2;
  • MUST use a single let declaration for each assignment
// good
let a = 1;
let b = 2;

// bad
let a = 1, b = 2;
  • lets SHOULD be defined inline where they are needed and don't need to be placed at the beginning of the scoped block
sortColumn( column ) {
    let columnTitle = Ember.get( column, 'title' );

    columnTitle = columnTitle + 'test';

    let firstName = Ember.get( column, 'firstName' );
    let lastName = Ember.get( column, 'lastName' );
}

Whitespace

  • MUST use soft tabs set to 4 spaces
// good
function() {
∙∙∙∙const name;
}

// bad
function() {
⇥const name;
}
  • MUST place 1 space before a leading brace ({)
// good
obj.set( 'foo', {
    foo: 'bar'
});

test( 'foo-bar', function() {
    ...
});

// bad
obj.set( 'foo',{
    foo: 'bar'
});

test( 'foo-bar', ()=>{
    ...
});
  • MUST NOT use spaces before semicolons
// good
const foo = {};

// bad
const foo = {} ;
  • MUST keep parentheses adjacent to the function name when declared or called
// good
function foo( bar ) {
    ...
}

foo( 1 );

// bad
function foo (bar) {
    ...
}

foo ( 1 );
  • Operators by default MUST have spaces around them ...EXCEPT...
    • Non-word unary operators MUST NOT
// good
if ( !available ) {
    ...
}

if ( !( 'foo' in obj ) ) {
    ...
}

i++;

let j = -x;

delete myVariable;

let a = b;


// bad
if ( ! available ) {
    ...
}

if ( ! ( 'foo' in obj ) ) {
    ...
}

i ++;

let j = - x;

let a=b;
  • MUST NOT aligning on colons and equal signs
// good
let animal = 'cat';
let car = 'fast';

callThisFunction({
    display: true,
    collapsible: false,
    order: 'ASC'
});

// bad
let animal = 'cat';
let car    = 'fast';

callThisFunction({
    display     : true,
    collapsible : false,
    order       : 'ASC'
});
  • Lines MUST NOT contain any trailing whitespaces

  • Blank lines MUST NOT contain any whitespace (not the same thing as a newline)

  • It is RECOMMENDED the following deviations be observed:

// Function accepting an array, no space
foo([ 'alpha', 'beta' ]);

// Function accepting an object, no space
foo({
    a: 'alpha',
    b: 'beta'
});

Newlines

  • Arrays and Objects can be defined with all of their elements or properties on a single line, but MUST be kept to 80 characters or less in length

  • Once a newline is introduced into the definition of the Array or Object then ALL elements or properties MUST be defined on a newline

// good

let test = [ 'okay', 'good' ];

let test = [
    'okay',
    'good'
];

let test = { okay: true };

let test = {
    okay: true
};

let test = { okay: true, wrong: false };

let test = {
    okay: true,
    wrong: false
};

let test = [
    'okay',
    { okay: true }
];

let test = {
    bindings: [
        'okay'
    ]
};


// bad

let test = [ 'okay', 'nope'
];

let test = [
    'okay', { okay: false }
];

let test = { okay: false
};

Naming Conventions

  • MUST be descriptive with naming
// good
function checkValidKey( key, value = 0 ) {
    ...
}

// bad
function check( k, v = 0 ) {
    ...
}
  • SHOULD name collection objects in either plural form or with an appended plural term (i.e., "items", "collection")
let item = {};
let items = [ item ];

let hardware = new Hardware();
let hardwareCollection = [ hardware, new Hardware() ];
  • Acronyms MUST be treated as words when in names
// Good
let createHtmlSnippet;

// Bad
let createHTMLSnippet;
  • Only industry-standard, unambiguous abbreviations MUST be used
    • When used, abbreviations MUST be used universally in the entire codebase, or not at all
    • When in doubt about clarity, you MUST NOT abbreviate
    • If you would expand the abbreviation when saying it out loud, you MUST NOT abbreviate
// Good
var http, tcp, ip;

// Bad
var hw, sw, vg; // hardware, software, virtual guest
  • Names MUST be as explicit as necessary to avoid any ambiguity in context. When there are two similar names, at least one of them MUST call out exactly what differentiates it.
// Contrived example copies items from `virtualGuests` to another array
let virtualGuests;
let filteredVirtualGuests;
  • camelCase MUST be used for functions, objects, instances, let, const, etc
// good
const presenceValidator = true;

let fooBar = function fooBar() {
    ...
}
  • PascalCase MUST only be used for constructors, classes or enums
// good
class Validator {
    constructor( options ) {
        this.rules = options.rules;
    }
}

const presenceValidator = new Validator({
    rules: {}
});

/* @enum {String} */
const ColumnSize = Object.freeze({
    LARGE: 'large',
    MEDIUM: 'medium'
});

// bad
function Validate (options ) {
    return options === true;
}

const validatedItem = Validate( item );

/* @enum {String} */
const columnSize = Object.freeze({
    LARGE: 'large',
    MEDIUM: 'medium'
});
  • Imports that are either module (namespaces) or classes MUST be capitalized. All others MUST be lowercased.
// good
import Ember from 'ember';
import Route from 'app/base/route';
import ModalManager from 'sl-ember-components/mixins/sl-modal-manager';
import PreferenceAdapter from 'app/adapters/preference';
import config from 'app/config/environment';

var translations = require( 'translations/en' );

Line Lengths

  • The number of characters per line MUST NOT exceed 120 and 80 SHOULD be considered the preferred limit.

Constructors

  • MUST use class instead of manipulating prototype
// good
class Car {
    constructor( make = 'Tesla' ) {
        this.make = make;
        this.position = { x: 0, y: 0 };
    }

    move( x = 0, y = 0 ) {
        this.position = { x, y };

        return this.position;
    }
}

// bad
function Car( make = 'Tesla' ) {
    this.make = make;
    this.position = { x: 0, y: 0 };
}

Car.prototype.move = function( x = 0, y = 0 ) {
    this.position = { x, y };

    return this.position;
}
  • MUST use extends for inheritance
class HondaCivic extends Car {
    constructor() {
        super( 'Honda' );
    }
}

Objects

  • MUST use literal form for object creation
// good
let doug = {};

// bad
let doug = new Object();
  • MUST pad single-line objects with white-space
// good
let rob = { likes: 'ember' };

// bad
let rob = {hates: 'spiders'};

Strings

  • SHOULD use single quotes, and use double quotes to avoid escaping
// good:
const foo = 'bar';
const baz = "What's this?";

// bad
const foo = "bar";
  • SHOULD use templates strings when constructing strings with dynamic values
const prefix = 'Hello';
const suffix = 'and have a good day.';

// good
return `${prefix} world, ${suffix}`;

// bad
return prefix + ' world, ' + suffix;

Arrays

  • MUST use literal form for array creation (unless you know the exact length)
// good
const foo = [ 1, 2, 3 ];
const bar = new Array(3);
let list = [];

// bad
const foo = new Array();
const bar = new Array( 1, 2, 3 );
let list = new Array();
  • MUST use new Array if you know the exact length of the array and know that its length will not change
const foo = new Array(16);
  • MUST use push to add an item to an array
const foo = [];
const { length } = foo;

// good
foo.push( 'bar' );

// bad
foo[length] = 'bar';
  • MUST use spread
// join 2 arrays
const foo = [ 0, 1, 2 ];
const bar = [ 3, 4, 5 ];

foo.push( ...bar );

// avoid using `Function.prototype.apply`
const values = [ 25, 50, 75, 100 ];

// good
const max = Math.max.apply( Math, values );

// better
const max = Math.max( ...values );
  • MUST join single line array items with a space
// good
const foo = [ 'a', 'b', 'c' ];

// bad
const foo = ['a','b','c'];
  • MUST use array destructuring
const arr = [ 1, 2, 3, 4 ];

// good
const [ head, ...tail ] = arr;

// bad
const head = arr.shift();
const tail = arr;

Properties

  • MUST use property value shorthand
const name = 'Derek Zoolander';
const age = 25;

// good
const foo = {
    name,
    age
};

// bad
const foo = {
    name: name,
    age: age
};
  • MUST not use quotations (single or double) when defining property names
// good
const foo = {
    bar: 'bar'
};

// bad
const foo = {
    'bar': 'bar',
    "baz": "baz"
};
  • MUST use dot-notation when accessing properties
const foo = {
    bar: 'bar'
};

// good
foo.bar;

// bad
foo[ 'bar' ];
  • MUST use [] when accessing properties via a variable
const propertyName = 'bar';
const foo = {
    bar: 'bar'
};

// good
foo[propertyName];

// do you even javascript
foo.propertyName;
  • MUST use object destructuring when accessing multiple properties on an object
// good
function foo( person ) {
    const { name, age, height } = person;

    return `${name} is ${age} years old and ${height} tall.`
}

// bad
function foo( person ) {
    const name = person.name;
    const age = person.age;
    const height = person.height;

    return `${name} is ${age} years old and ${height} tall.`
}

Functions

  • MUST use object method shorthand
// good
const foo = {
    value: 0,

    bar( value ) {
        return foo.value + value;
    }
};

// bad
const foo = {
    value: 0,

    bar: function bar( value ) {
        return foo.value + value;
    }
};
  • MUST use scope to lookup functions (not variables)
// good
function foo() {
    function bar() {
        ...
    }

    bar();
}

// bad
function foo() {
    const bar = function bar() {
        ...
    }

    bar();
}
  • MUST use fat arrows to preserve this when using function expressions or anonymous functions
const foo = {
    base: 0,

    // good
    bar( items ) {
        return items.map( ( item ) => {
            return this.base * item.value;
        });
    },

    // good
    bar( items ) {
        return items.map( ( item ) => this.base + item.value );
    },

    // bad
    bar( items ) {
        const _this = this;

        return items.map( function( item ) {
            return _this.base + item.value;
        });
    }
};
  • MUST always use parentheses around arguments
// good
[ 1, 2, 3 ].map( ( x ) => x * x );

// bad
[ 1, 2, 3 ].map( x => x * x );
  • If the function body fits on one line, feel free to omit the braces and use implicit return. Otherwise, add the braces and use a return statement.
// good
[ 1, 2, 3 ].map( ( x ) => x * x );

// good
[ 1, 2, 3 ].map( ( x ) => {
    return { number: x };
});
  • It is RECOMMENDED to use fat arrows over the more verbose anonymous function syntax

Function Arguments

  • MUST never use arguments – use rest instead
// good
function foo( ...args ) {
    return args.join( '' );
}

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

    return args.join( '' );
}
  • arguments object must not be passed or leaked anywhere. See the reference.

  • MUST NOT re-assign the arguments

// good
// Use a new variable if you need to assign the value of an argument
function fooBar( opt ) {
    let _opt = opt;

    _opt = 3;
}

// bad
function fooBar() {
    arguments = 3;
}

// bad
function fooBar( opt ) {
    opt = 3;
}
  • MUST use default params instead of mutating them
// good
function fooBar( obj = {}, key = 'id', value = 0 ) {
    if ( key ) {
        return obj[key];
    } else {
        obj[key] = value;
        return obj[key];
    }
}

// bad
function fooBar( obj, key, value ) {
    obj = obj || {};
    key = key || 'id';
    ...
}

Enumerables

Although enumerables don't exist natively in JavaScript yet, the functionality to create an object structure analogous to an enum exists using a combination of ES2015's const with Object.freeze().

const EnumType = Object.freeze({
    FIRST: 'first',
    SECOND: 'second',
    THIRD: 'third'
});

When defining an enum type in an ES2015 module intended to be used by other consuming code, you should then export the enum in a block with the enum.

export { EnumType };