Skip to content

Latest commit

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..

README.md

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. 🚫

vara=1;varb=2;

🎉 Yep! 🎉

consta=1;constb=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. 🚫

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

🎉 Yep! 🎉

letcount=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.{leta=1;constb=1;}console.log(a);// ReferenceError: a is not definedconsole.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. 🚫

constitem=newObject();

🎉 Yep! 🎉

constitem={};

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. 🚫

constatom={value: 1,addValue: function(value){returnatom.value+value;},};

🎉 Yep! 🎉

constatom={value: 1,addValue(value){returnatom.value+value;},};

Object Property

🚫 Nope. 🚫

constgeneralLeiaOrgana='General Leia Organa';constobj={generalLeiaOrgana: generalLeiaOrgana,};

🎉 Yep! 🎉

constgeneralLeiaOrgana='General Leia Organa';constobj={
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. 🚫

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

🎉 Yep! 🎉

constobj={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! 🎉

// Goodconsole.log(Object.prototype.hasOwnProperty.call(object,key));// Bestconsthas=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` ಠ_ಠconstoriginal={a: 1,b: 2};constcopy=Object.assign(original,{c: 3});deletecopy.a;// So does this!// Works but not preferredconstoriginal={a: 1,b: 2};constcopy=Object.assign({},original,{c: 3});// copy => { a: 1, b: 2, c: 3 }

🎉 Yep! 🎉

constoriginal={a: 1,b: 2};constcopy={ ...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. 🚫

constitems=newArray();

🎉 Yep! 🎉

constitems=[];

Resources

3.2 Adding Items To Arrays

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

Examples

🚫 Nope. 🚫

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

🎉 Yep! 🎉

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

Resources

3.3 Array Shallow-Copy

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

Why? Better overall performance.

Examples

🚫 Nope. 🚫

// Too slowconstanimals=['ant','bison','camel','duck','elephant'];constlen=animals.length;constanimalsCopy=[];leti;for(i=0;i<len;i++){animalsCopy[i]=animals[i];}// Works but is not preferredconstanimals=['ant','bison','camel','duck','elephant'];constanimalsCopy=animals.slice();

🎉 Yep! 🎉

constanimals=['ant','bison','camel','duck','elephant'];constanimalsCopy=[...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. 🚫

constparagraphs=document.querySelectorAll('p');constnodes=Array.from(paragraphs);

🎉 Yep! 🎉

constparagraphs=document.querySelectorAll('p');constnodes=[...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. 🚫

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

🎉 Yep! 🎉

constarrLike={0: 'foo',1: 'bar',2: 'baz',length: 3};constarr=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. 🚫

constiterable='Hello there!';constupperCase=letter=>letter.toUpperCase();constupperCaseLetters=Array.from(iterable,upperCase);

🎉 Yep! 🎉

constiterable='Hello there!';constupperCase=letter=>letter.toUpperCase();constupperCaseLetters=[...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'){returnauthor==='Harper Lee';}else{returnfalse;}});

🎉 Yep! 🎉

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

🎉 Also good! 🎉

[1,2,3].map((x)=>{consty=x+1;returnx*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. 🚫

functiongetFullName(user){constfirstName=user.firstName;constlastName=user.lastName;return`${firstName}${lastName}`;}

🎉 Yep! 🎉

// GoodfunctiongetFullName(user){const{ firstName, lastName }=user;return`${firstName}${lastName}`;}// BestfunctiongetFullName({ 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!constarr=[1,2,3,4];constfirst=arr[0];constsecond=arr[1];constrest=arr.slice(2);console.log(first);// 1console.log(second);// 2console.log(rest);// [3, 4]// This works great also!constarr=[1,2,3,4];const[first,second, ...rest]=arr;console.log(first);// 1console.log(second);// 2console.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. 🚫

functionprocessInput(input){return[left,right,top,bottom];}// the caller needs to think about the order of return dataconst[left,__,top]=processInput(input);

🎉 Yep! 🎉

functionprocessInput(input){return{ left, right, top, bottom };}// the caller selects only the data they needconst{ 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.constname="Cloud Four";// Template literals should contain interpolation or newlines.constname=`Cloud Four`;// This string has a literal single quote!constfoo='What\'s for dinner?';

🎉 Yep! 🎉

constname='Cloud Four';// It's okay to use double quotes here.constfoo="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. 🚫

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

🎉 Yep! 🎉

functionsayHi(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();functionlogFoo(){console.log("foo");}

We would prefer one of the following patterns:

// Define the function before callingfunctionlogFoo(){console.log("foo");}logFoo();
// Import the functionimport{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.)functionmain(){thing1();thing2();thing3();thing4();}functionthing1(){}functionthing2(){}functionthing3(){}functionthing4(){}main();

Another option is to move helper functions to modules:

// Import helpers so they're defined up frontimport{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. 🚫

functionfoo(name,options,arguments){// ...}

🎉 Yep! 🎉

functionfoo(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. 🚫

functionconcatenateAll(){constargs=Array.prototype.slice.call(arguments);returnargs.join('');}// Slow performancefunctionconcatenateAll(){constargs=Array.from(arguments);returnargs.join('');}

🎉 Yep! 🎉

functionconcatenateAll(...args){returnargs.join('');}

Resources

6.4 Function Default Parameters

Use function default parameter syntax rather than mutating function arguments.

Examples

🚫 Nope. 🚫

functiondoThings(opts){// If opts is falsey it can introduce bugs.opts=opts||{};// ...}functiondoThings(opts){if(opts===undefined){opts={};}// ...}

🎉 Yep! 🎉

functiondoThings(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. 🚫

letb=1;// Eek!functioncount(a=b++){console.log(a);}count();// 1count();// 2count(3);// 3count();// 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. 🚫

varadd=newFunction('a','b','return a + b');varsubtract=Function('a','b','return a - b');

🎉 Yep! 🎉

varx=function(a,b){returna+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. 🚫

functionfoo(bar){bar=13;}functionfoo(bar){bar++;}

🎉 Yep! 🎉

functionfoo(bar){varbaz=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. 🚫

constargs=[1,2,3,4];Math.max.apply(Math,args);new(Function.prototype.bind.apply(Date,[null,2016,8,5]));

🎉 Yep! 🎉

constargs=[1,2,3,4];Math.max(...args);newDate(...[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