A mostly reasonable approach to JavaScript
Other Style Guides
-
1.1 Use single quotes
''
for strings. eslint:quotes
// bad const name = "Capt. Kirk"; // bad - template literals should contain interpolation or newlines const name = `Capt. Kirk`; // good const name = 'Capt. Kirk';
-
1.2 Strings that cause the line to go over 100 characters should not be written across multiple lines using string concatenation.
Why? Broken strings are painful to work with and make code less searchable.
// bad const errorMessage = 'This is a super long error that was thrown because \ of Batman. When you stop to think about how Batman had anything to do \ with this, you would get nowhere \ fast.'; // bad const errorMessage = 'This is a super long error that was thrown because ' + 'of Batman. When you stop to think about how Batman had anything to do ' + 'with this, you would get nowhere fast.'; // good const errorMessage = 'This is a super long error that was thrown because of Batman. When you stop to think about how Batman had anything to do with this, you would get nowhere fast.';
-
1.3 When programmatically building up strings, use template strings instead of concatenation. eslint:
prefer-template
template-curly-spacing
Why? Template strings give you a readable, concise syntax with proper newlines and string interpolation features.
// bad function sayHi( name ) { return 'How are you, ' + name + '?'; } // bad function sayHi( name ) { return [ 'How are you, ', name, '?' ].join(); } // bad function sayHi( name ) { return `How are you, ${name}?`; } // good function sayHi( name ) { return `How are you, ${ name }?`; }
-
1.5 Do not unnecessarily escape characters in strings. eslint:
no-useless-escape
Why? Backslashes harm readability, thus they should only be present when necessary.
// bad const foo = '\'this\' \i\s \"quoted\"'; // good const foo = '\'this\' is "quoted"'; const foo = `my name is '${ name }'`;
-
2.1 Use
const
for all of your references; avoid usingvar
. eslint:prefer-const
,no-const-assign
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;
-
2.2 If you must reassign references, use
let
instead ofvar
. eslint:no-var
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; }
-
2.3 Note that both
let
andconst
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
-
3.1 Use the literal syntax for object creation. eslint:
no-new-object
// bad const item = new Object(); // good const item = {};
-
3.2 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 }`; } // bad const obj = { id: 5, name: 'San Francisco', }; obj[ getKey( 'enabled' ) ] = true; // good const obj = { id: 5, name: 'San Francisco', [ getKey( 'enabled' ) ]: true, };
-
3.3 Use object method shorthand. eslint:
object-shorthand
// bad const atom = { value: 1, addValue: function( value ) { return atom.value + value; }, }; // good const atom = { value: 1, addValue( value ) { return atom.value + value; }, };
-
3.4 Use property value shorthand. eslint:
object-shorthand
Why? It is shorter and descriptive.
const lukeSkywalker = 'Luke Skywalker'; // bad const obj = { lukeSkywalker: lukeSkywalker, }; // good const obj = { lukeSkywalker, };
-
3.5 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'; const 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, };
-
3.6 Only quote properties that are invalid identifiers. eslint:
quote-props
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, };
-
3.7 Do not call
Object.prototype
methods directly, such ashasOwnProperty
,propertyIsEnumerable
, andisPrototypeOf
. eslint:no-prototype-builtins
Why? These methods may be shadowed by properties on the object in question - consider
{ hasOwnProperty: false }
- or, the object may be a null object (Object.create(null)
).// bad console.log( object.hasOwnProperty( key ) ); // good console.log( Object.prototype.hasOwnProperty.call( object, key ) ); // best const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope. /* or */ import has from 'has'; // https://www.npmjs.com/package/has // ... console.log( has.call( object, key ) );
-
3.8 Prefer the object spread operator over
Object.assign
to shallow-copy objects. Use the object rest operator to get a new object with certain properties omitted.// very bad const original = { a: 1, b: 2 }; const copy = Object.assign( original, { c: 3 } ); // this mutates `original` ಠ_ಠ delete copy.a; // so does this // bad const original = { a: 1, b: 2 }; const copy = Object.assign( {}, original, { c: 3 } ); // copy => { a: 1, b: 2, c: 3 } // good const original = { a: 1, b: 2 }; const copy = { ...original, c: 3 }; // copy => { a: 1, b: 2, c: 3 } const { a, ...noA } = copy; // noA => { b: 2, c: 3 }
-
4.1 Use the literal syntax for array creation. eslint:
no-array-constructor
// bad const items = new Array(); // good const items = [];
-
4.2 Use Array#push instead of direct assignment to add items to an array.
const someStack = []; // bad someStack[ someStack.length ] = 'abracadabra'; // good someStack.push( 'abracadabra' );
-
4.3 Use array spreads
...
to copy arrays.// bad const len = items.length; const itemsCopy = []; let i; for ( i = 0; i < len; i += 1 ) { itemsCopy[ i ] = items[ i ]; } // good const itemsCopy = [ ...items ];
-
4.4 To convert an iterable object to an array, use spreads
...
instead ofArray.from
.const foo = document.querySelectorAll( '.foo' ); // good const nodes = Array.from( foo ); // best const nodes = [ ...foo ];
-
4.5 Use
Array.from
for converting an array-like object to an array.const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 }; // bad const arr = Array.prototype.slice.call( arrLike ); // good const arr = Array.from( arrLike );
-
4.6 Use
Array.from
instead of spread...
for mapping over iterables, because it avoids creating an intermediate array.// bad const baz = [ ...foo ].map( bar ); // good const baz = Array.from( foo, bar );
-
4.7 Use return statements in array method callbacks. It’s ok to omit the return if the function body consists of a single statement returning an expression without side effects, following 8.2. eslint:
array-callback-return
// good [ 1, 2, 3 ].map( ( x ) => { const y = x + 1; return x * y; } ); // good [ 1, 2, 3 ].map( x => x + 1 ); // bad - no returned value means `acc` becomes undefined after the first iteration [ [ 0, 1 ], [ 2, 3 ], [ 4, 5 ] ].reduce( ( acc, item, index ) => { const flatten = acc.concat( item ); acc[ index ] = flatten; } ); // good [ [ 0, 1 ], [ 2, 3 ], [ 4, 5 ] ].reduce( ( acc, item, index ) => { const flatten = acc.concat( item ); acc[ index ] = flatten; return flatten; } ); // bad inbox.filter( ( msg ) => { const { subject, author } = msg; if ( subject === 'Mockingbird' ) { return author === 'Harper Lee'; } else { return false; } }); // good inbox.filter( ( msg ) => { const { subject, author } = msg; if ( subject === 'Mockingbird' ) { return author === 'Harper Lee'; } return false; } );
-
4.8 Use line breaks after open and before close array brackets if an array has multiple lines
// bad const arr = [ [ 0, 1 ], [ 2, 3 ], [ 4, 5 ], ]; const objectInArray = [ { id: 1, }, { id: 2, } ]; const numberInArray = [ 1, 2, ]; // good const arr = [ [ 0, 1 ], [ 2, 3 ], [ 4, 5 ] ]; const objectInArray = [ { id: 1, }, { id: 2, }, ]; const numberInArray = [ 1, 2, ];
-
5.1 Leading commas: Nope. eslint:
comma-style
// 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', };
-
5.2 Additional trailing comma: Yup. eslint:
comma-dangle
Why? This leads to cleaner git diffs. Also, transpilers like Babel will remove the additional trailing comma in the transpiled code which means you don’t have to worry about the trailing comma problem in legacy browsers.
// 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', ]; // bad function createHero( firstName, lastName, inventorOf ) { // does nothing } // good function createHero( firstName, lastName, inventorOf, ) { // does nothing } // good (note that a comma must not appear after a "rest" element) function createHero( firstName, lastName, inventorOf, ...heroArgs ) { // does nothing } // bad createHero( firstName, lastName, inventorOf ); // good createHero( firstName, lastName, inventorOf, ); // good (note that a comma must not appear after a "rest" element) createHero( firstName, lastName, inventorOf, ...heroArgs );
-
// bad function foo() { ∙∙∙∙let name; } // good function foo() { » let name; }
-
6.2 Place 1 space before the leading brace. eslint:
space-before-blocks
// 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', } );
-
6.3 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. eslint:keyword-spacing
// bad if( isJedi ) { fight (); } // good if ( isJedi ) { fight(); } // bad function fight () { console.log ( 'Swooosh!' ); } // good function fight() { console.log( 'Swooosh!' ); }
-
6.4 Set off operators with spaces. eslint:
space-infix-ops
// bad const x=y+5; // good const x = y + 5;
-
6.5 End files with a single newline character. eslint:
eol-last
// bad import { es6 } from './styleguide'; // ... export default es6;
// bad import { es6 } from './styleguide'; // ... export default es6;↵ ↵
// good import { es6 } from './styleguide'; // ... export default es6;↵
-
6.6 Avoid spaces before commas and require a space after commas. eslint:
comma-spacing
// bad const foo = 1,bar = 2; const arr = [ 1 , 2 ]; // good const foo = 1, bar = 2; const arr = [ 1, 2 ];