Skip to content

Latest commit

 

History

History
1282 lines (821 loc) · 24.1 KB

README.md

File metadata and controls

1282 lines (821 loc) · 24.1 KB

JavaScript Guide

A mostly reasonable approach to JavaScript, inspired by the Airbnb JavaScript Style Guide.

A note on performance vs readability

The end-user experience always come first which means performance should always be top-of-mind. The JSPerf examples used in this guide do not use large datasets. If you find yourself working with large datasets and the suggested approach based on this guide performs slower, don't be afraid to push back on a per-project basis (see Performance vs Readability).

Table of Contents

  1. Variables
  2. Objects
  3. Arrays
  4. Destructring
  5. Strings
  6. Functions

Variables

1.1 Prefer Constants

Use const for all of your references; avoid using var.

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

Examples

🚫 Nope. 🚫

var a = 1;
var b = 2;

🎉 Yep! 🎉

const a = 1;
const b = 2;

Resources

1.2 Reassigning References

If you must reassign references, use let instead of var.

Why? let is block-scoped rather than function-scoped like var. Function-scoped variables are hoisted which can lead to bugs if you are not careful. Using block-scoped variables makes our code more predictable by giving the variable an explicit scope.

Examples

🚫 Nope. 🚫

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

🎉 Yep! 🎉

let count = 1;
if (true) {
  count += 1;
}

Resources

1.3 Block Scope

Note that both let and const are block-scoped.

Examples

// Both `const` and `let` only exist in the blocks they are defined in.
{
  let a = 1;
  const b = 1;
}
console.log(a); // ReferenceError: a is not defined
console.log(b); // ReferenceError: b is not defined

⇧ top


Objects

2.1 Object Creation

Use the literal syntax for object creation.

Why? While there are no performance differences between the two approaches, the byte savings and conciseness of the object literal form is what has made it the de facto way of creating new objects.

Examples

🚫 Nope. 🚫

const item = new Object();

🎉 Yep! 🎉

const item = {};

Resources

2.2 Object Shorthand Syntax

Use object shorthand syntax for both methods and property values.

Why? ECMAScript 6 provides a concise form for defining object literal methods and properties. This syntax can make defining complex object literals much cleaner.

Object Method

🚫 Nope. 🚫

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

🎉 Yep! 🎉

const atom = {
  value: 1,
  addValue(value) {
    return atom.value + value;
  },
};

Object Property

🚫 Nope. 🚫

const generalLeiaOrgana = 'General Leia Organa';

const obj = {
  generalLeiaOrgana: generalLeiaOrgana,
};

🎉 Yep! 🎉

const generalLeiaOrgana = 'General Leia Organa';

const obj = {
  generalLeiaOrgana,
};

Resources

2.3 Object Quoted Properties

Only quote properties that are invalid identifiers.

Why? In general we consider it subjectively easier to read. It improves syntax highlighting, and is also more easily optimized by many JS engines.

Examples

🚫 Nope. 🚫

const obj = {
  'foo': 3,
  'bar': 4,
  'data-blah': 5,
};

🎉 Yep! 🎉

const obj = {
  foo: 3,
  bar: 4,
  'data-blah': 5,
};

Resources

2.4 Object Prototype Methods

Do not call Object.prototype methods directly, such as hasOwnProperty, propertyIsEnumerable, and isPrototypeOf.

Why? In ECMAScript 5.1, Object.create was added, which enables the creation of objects with a specified [[Prototype]]. Object.create(null) is a common pattern used to create objects that will be used as a Map. This can lead to errors when it is assumed that objects will have properties from Object.prototype.

Examples

🚫 Nope. 🚫

console.log(object.hasOwnProperty(key));

🎉 Yep! 🎉

// Good
console.log(Object.prototype.hasOwnProperty.call(object, key));

// Best
const has = Object.prototype.hasOwnProperty; // cache the lookup once, in module scope.
console.log(has.call(object, key));

Resources

2.5 Object Shallow-Copy

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.

Why? Object spread is a declarative alternative which may perform better than the more dynamic, imperative Object.assign.

Examples

🚫 Nope. 🚫

// This mutates `original` ಠ_ಠ
const original = { a: 1, b: 2 };
const copy = Object.assign(original, { c: 3 });
delete copy.a; // So does this!

// Works but not preferred
const original = { a: 1, b: 2 };
const copy = Object.assign({}, original, { c: 3 }); // copy => { a: 1, b: 2, c: 3 }

🎉 Yep! 🎉

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 }

Resources

⇧ top


Arrays

3.1 Array Creation

Use the literal syntax for array creation.

Why? Use of the Array constructor to construct a new array is generally discouraged in favor of array literal notation because of the single-argument pitfall and because the Array global may be redefined.

Examples

🚫 Nope. 🚫

const items = new Array();

🎉 Yep! 🎉

const items = [];

Resources

3.2 Adding Items To Arrays

Use Array.prototype.push() instead of direct assignment to add items to an array.

Examples

🚫 Nope. 🚫

const someStack = [];
someStack[someStack.length] = 'abracadabra';

🎉 Yep! 🎉

const someStack = [];
someStack.push('abracadabra');

Resources

3.3 Array Shallow-Copy

Use array spread syntax ... to shallow-copy arrays.

Why? Better overall performance.

Examples

🚫 Nope. 🚫

// Too slow
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
const len = animals.length;
const animalsCopy = [];
let i;

for (i = 0; i < len; i ++) {
  animalsCopy[i] = animals[i];
}

// Works but is not preferred
const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
const animalsCopy = animals.slice();

🎉 Yep! 🎉

const animals = ['ant', 'bison', 'camel', 'duck', 'elephant'];
const animalsCopy = [...animals];

Resources:

3.4 Arrays From Iterables

To convert an iterable object (e.g. NodeList) to an array, use array spread syntax ... instead of Array.from.

Why? Better performance.

Examples

🚫 Nope. 🚫

const paragraphs = document.querySelectorAll('p');
const nodes = Array.from(paragraphs);

🎉 Yep! 🎉

const paragraphs = document.querySelectorAll('p');
const nodes = [...paragraphs];

Note

If using Babel with @babel/preset-env with option loose:true, and are transpiling to older targets in a .browserlistrc, you may need to add the following to your Babel config (e.g., babel.config.js):

plugins: [
  ...,
  '@babel/plugin-transform-spread',
  ...
]

(The default option for this plugin is loose:false, which will override the global setting)

Resources

3.5 Arrays from Array-Like Objects

Use Array.from for converting an array-like object to an array.

Why? Not only is it easier to read/type but it also performs better.

Examples

🚫 Nope. 🚫

const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
const arr = Array.prototype.slice.call(arrLike);

🎉 Yep! 🎉

const arrLike = { 0: 'foo', 1: 'bar', 2: 'baz', length: 3 };
const arr = Array.from(arrLike);

Resources

3.6 Mapping Over Iterables

Use array spread syntax ... instead of Array.from for mapping over iterables.

Why? Overall better performance.

Examples

🚫 Nope. 🚫

const iterable = 'Hello there!';
const upperCase = letter => letter.toUpperCase();
const upperCaseLetters = Array.from(iterable, upperCase);

🎉 Yep! 🎉

const iterable = 'Hello there!';
const upperCase = letter => letter.toUpperCase();
const upperCaseLetters = [...iterable].map(upperCase);

Resources

3.7 Array Callback Return

Use return statements in array method callbacks. It’s okay to omit the return if the function body consists of a single statement returning an expression without side effects.

Examples

🚫 Nope. 🚫

inbox.filter(msg => {
  const { subject, author } = msg;
  if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
  } else {
    return false;
  }
});

🎉 Yep! 🎉

inbox.filter(msg => {
  const { subject, author } = msg;
  if (subject === 'Mockingbird') {
    return author === 'Harper Lee';
  }

  return false;
});

🎉 Also good! 🎉

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

// The return can be omitted here.
[1, 2, 3].map(x => x + 1);

Resources

⇧ top


Destructuring

4.1 Object Destructuring

Use object destructuring when accessing and using multiple properties of an object.

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

Examples

🚫 Nope. 🚫

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

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

🎉 Yep! 🎉

// Good
function getFullName(user) {
  const { firstName, lastName } = user;
  return `${firstName} ${lastName}`;
}

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

Resources

4.2 Array Destructuring

How you destructure an array depends on your situation. Below are a couple of ways to complete the same task.

Examples

// This works!
const arr = [1, 2, 3, 4];
const first = arr[0];
const second = arr[1];
const rest = arr.slice(2);

console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4]

// This works great also!
const arr = [1, 2, 3, 4];
const [first, second, ...rest] = arr;

console.log(first); // 1
console.log(second); // 2
console.log(rest); // [3, 4]

Note: For performance reasons, strongly consider use of the @babel/plugin-transform-destructuring plugin when using array destructuring.

Resources

4.3 Destructuring for Multiple Return Values

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.

Examples

🚫 Nope. 🚫

function processInput(input) {
  return [left, right, top, bottom];
}

// the caller needs to think about the order of return data
const [left, __, top] = processInput(input);

🎉 Yep! 🎉

function processInput(input) {
  return { left, right, top, bottom };
}

// the caller selects only the data they need
const { left, top } = processInput(input);

⇧ top


Strings

5.1 Quotes

Use single quotes '' for strings. The exception is if a string includes a literal ' single quote, use double quotes " instead.

Examples

🚫 Nope. 🚫

// Should be single quote.
const name = "Cloud Four";

// Template literals should contain interpolation or newlines.
const name = `Cloud Four`;

// This string has a literal single quote!
const foo = 'What\'s for dinner?';

🎉 Yep! 🎉

const name = 'Cloud Four';

// It's okay to use double quotes here.
const foo = "What's for dinner?";

Resources

5.2 Template Literals

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

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

Examples

🚫 Nope. 🚫

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

function sayHi(name) {
  return ['How are you, ', name, '?'].join();
}

🎉 Yep! 🎉

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

Resources

5.3 Eval

Never use eval() on a string, it opens too many vulnerabilities.

Resources

⇧ top


Functions

6.1 Avoid Function Hoisting

Although it is possible to call functions before they are defined via hoisting we prefer to avoid this pattern in our code as it can be confusing.

Examples

Although this code works properly it may be more confusing and we generally avoid it:

logFoo();

function logFoo() {
  console.log("foo");
}

We would prefer one of the following patterns:

// Define the function before calling
function logFoo() {
  console.log("foo");
}

logFoo();
// Import the function
import {logFoo} from './log-foo.js';

logFoo();

In files that call a number of helper functions it can be helpful to move those functions to modules, or start the file with a main function that provides a summary of the steps taken in the file. For example:

// The `main` function contains an overview of the file's logic.
// (`main` could be switched to a more meaningful name in context.)
function main() {
  thing1();
  thing2();
  thing3();
  thing4();
}

function thing1() {}
function thing2() {}
function thing3() {}
function thing4() {}

main();

Another option is to move helper functions to modules:

// Import helpers so they're defined up front
import {thing1, thing2, thing3, thing4} from './helpers.js';

thing1();
thing2();
thing3();
thing4();

6.2 Function Arguments Parameter

Never name a function parameter arguments.

Why? This will take precedence over the arguments object that is given to every function scope.

Examples

🚫 Nope. 🚫

function foo(name, options, arguments) {
  // ...
}

🎉 Yep! 🎉

function foo(name, options, args) {
  // ...
}

Resources

6.3 Use Rest Syntax for Function Arguments Object

Use the rest syntax ...args instead of the arguments object.

Why? Rest arguments are a real Array, and not merely Array-like as the arguments object is.

Examples

🚫 Nope. 🚫

function concatenateAll() {
  const args = Array.prototype.slice.call(arguments);
  return args.join('');
}

// Slow performance
function concatenateAll() {
  const args = Array.from(arguments);
  return args.join('');
}

🎉 Yep! 🎉

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

Resources

6.4 Function Default Parameters

Use function default parameter syntax rather than mutating function arguments.

Examples

🚫 Nope. 🚫

function doThings(opts) {
  // If opts is falsey it can introduce bugs.
  opts = opts || {};
  // ...
}

function doThings(opts) {
  if (opts === undefined) {
    opts = {};
  }
  // ...
}

🎉 Yep! 🎉

function doThings(opts = {}) {
  // ...
}

Resources

6.5 Function Default Parameter Side Effects

Avoid side effects with function default parameters.

Why? They are confusing to reason about.

Examples

🚫 Nope. 🚫

let b = 1;

// Eek!
function count(a = b++) {
  console.log(a);
}

count();  // 1
count();  // 2
count(3); // 3
count();  // 3

6.6 Function Constructor

Never use the Function constructor to create a new function.

Why? Creating a function in this way evaluates a string similarly to eval(), which opens vulnerabilities.

Examples

🚫 Nope. 🚫

var add = new Function('a', 'b', 'return a + b');

var subtract = Function('a', 'b', 'return a - b');

🎉 Yep! 🎉

var x = function (a, b) {
    return a + b;
};

Resources

6.7 Mutating Function Parameters

Never mutate function parameters.

Why? Manipulating objects passed in as parameters can cause unwanted variable side effects in the original caller.

Examples

🚫 Nope. 🚫

function foo(bar) {
    bar = 13;
}

function foo(bar) {
    bar++;
}

🎉 Yep! 🎉

function foo(bar) {
    var baz = bar;
}

Resources

6.8 Spread Syntax for Variadic Functions

Prefer the use of the spread syntax operator ... to call variadic functions (a function that accepts a variable number of arguments).

Why? It’s cleaner, you don’t need to supply a context, and it's easier to compose new when compared to using apply.

Examples

🚫 Nope. 🚫

const args = [1, 2, 3, 4];
Math.max.apply(Math, args);

new (Function.prototype.bind.apply(Date, [null, 2016, 8, 5]));

🎉 Yep! 🎉

const args = [1, 2, 3, 4];
Math.max(...args);

new Date(...[2016, 8, 5]);

Resources

⇧ top


Arrow Functions

TBD...

⇧ top


Classes & Constructors

TBD...

⇧ top


Modules

TBD...

⇧ top


Iterators and Generators

TBD...

⇧ top


Properties

TBD...

⇧ top


Variables

TBD...

⇧ top


Hoisting

TBD...

⇧ top


Comparison Operators & Equality

TBD...

⇧ top


Blocks

TBD...

⇧ top


Control Statements

TBD...

⇧ top


Comments

TBD...

⇧ top


Whitespace

TBD...

⇧ top


Commas

TBD...

⇧ top


Semicolons

TBD...

⇧ top


Type Casting & Coercion

TBD...

⇧ top


Naming Conventions

TBD...

⇧ top


Accessors

TBD...

⇧ top


Events

TBD...

⇧ top


jQuery

TBD...

⇧ top


ECMAScript 5 Compatibility

TBD...

⇧ top


ECMAScript 6+ (ES 2015+) Styles

TBD...

⇧ top


Standard Library

TBD...

⇧ top


Testing

TBD...

⇧ top


Performance

TBD...

⇧ top


Resources

TBD...

⇧ top


In the Wild

TBD...

⇧ top


Translation

TBD...

⇧ top


The JavaScript Style Guide Guide

TBD...

⇧ top


Contributors

TBD...

⇧ top


License

TBD...

⇧ top