JavaScript Cheatsheet
JavaScript Cheatsheet
If you like this content, you can ping me or follow me on Twitter :+1:
Introduction
Motivation
This document is a cheatsheet for JavaScript you will frequently encounter in
modern projects and most contemporary sample code.
This guide is not intended to teach you JavaScript from the ground up, but to help
developers with basic knowledge who may struggle to get familiar with modern
codebases (or let’s say to learn React for instance) because of the JavaScript
concepts used.
Besides, I will sometimes provide personal tips that may be debatable but will take
care to mention that it’s a personal recommendation when I do so.
Note: Most of the concepts introduced here are coming from a JavaScript language
update (ES2015, often called ES6). You can find new features added by this update
here; it’s very well done.
Complementary Resources
When you struggle to understand a notion, I suggest you look for answers on the
following resources:
Short explanation
Variables declared with const keyword can’t be reassigned, while let and var can.
I recommend always declaring your variables with const by default, but with let if
it is a variable that you need to mutate or reassign later.
var
var declared variables are function scoped, meaning that when a variable is created
in a function, everything in that function can access that variable. Besides, a
function scoped variable created in a function can’t be accessed outside this
function.
function myFunction() {
var myVar = "Nick";
console.log(myVar); // "Nick" - myVar is accessible inside the function
}
console.log(myVar); // Throws a ReferenceError, myVar is not accessible outside the
function.
Still focusing on the variable scope, here is a more subtle example:
function myFunction() {
var myVar = "Nick";
if (true) {
var myVar = "John";
console.log(myVar); // "John"
// actually, myVar being function scoped, we just erased the previous myVar
value "Nick" for "John"
}
console.log(myVar); // "John" - see how the instructions in the if block affected
this value
}
console.log(myVar); // Throws a ReferenceError, myVar is not accessible outside the
function.
Besides, var declared variables are moved to the top of the scope at execution.
This is what we call var hoisting.
var myVar;
console.log(myVar) // undefined -- no error raised
myVar = 2;
let
var and let are about the same, but let declared variables
function myFunction() {
let myVar = "Nick";
if (true) {
let myVar = "John";
console.log(myVar); // "John"
// actually, myVar being block scoped, we just created a new variable myVar.
// this variable is not accessible outside this block and totally independent
// from the first myVar created !
}
console.log(myVar); // "Nick", see how the instructions in the if block DID NOT
affect this value
}
console.log(myVar); // Throws a ReferenceError, myVar is not accessible outside the
function.
Now, what it means for let (and const) variables for not being accessible before
being assigned:
Note: Technically, let and const variables declarations are being hoisted too, but
not their assignation. Since they’re made so that they can’t be used before
assignation, it intuitively feels like there is no hoisting, but there is. Find out
more on this very detailed explanation here if you want to know more.
let myVar = 2;
let myVar = 3; // Raises a SyntaxError
const
const declared variables behave like let variables, but also they can’t be
reassigned.
To sum it up, const variables:
For objects:
const person = {
name: 'Nick'
};
person.name = 'John' // this will work ! person variable is not completely
reassigned, but mutated
console.log(person.name) // "John"
person = "Sandra" // raises an error, because reassignment is not allowed with
const declared variables
For arrays:
More concise
this is picked up from surroundings
implicit return
Sample code
Concision and implicit return
function double(x) { return x * 2; } // Traditional way
console.log(double(2)) // 4
const double = x => x * 2; // Same function written as an arrow function with
implicit return
console.log(double(2)) // 4
this reference
In an arrow function, this is equal to the this value of the enclosing execution
context. Basically, with arrow functions, you don’t have to do the “that = this”
trick before calling a function inside a function anymore.
function myFunc() {
this.myVar = 0;
setTimeout(() => {
this.myVar++;
console.log(this.myVar) // 1
}, 0);
}
Detailed explanation
Concision
Arrow functions are more concise than traditional functions in many ways. Let’s
review all the possible cases:
function double(x) {
return x * 2; // this function explicitly returns x * 2, *return* keyword is
used
}
In the traditional way of writing functions, the return was always explicit. But
with arrow functions, you can do implicit return which means that you don’t need to
use the keyword return to return a value.
Note: If your function does not return a value (with side effects), it doesn’t do
an explicit nor an implicit return.
Besides, if you want to implicitly return an object you must have parentheses
around it since it will conflict with the block braces:
const double = (x) => x * 2; // this arrow function only takes one parameter
Parentheses around the parameter can be avoided:
const double = x => x * 2; // this arrow function only takes one parameter
No arguments
When there is no argument provided to an arrow function, you need to provide
parentheses, or it won’t be valid syntax.
In an arrow function, this is equal to the this value of the enclosing execution
context. What it means is that an arrow function doesn’t create a new this, it
grabs it from its surrounding instead.
Without arrow function, if you wanted to access a variable from this in a function
inside a function, you had to use the that = this or self = this trick.
function myFunc() {
this.myVar = 0;
var that = this; // that = this trick
setTimeout(
function() { // A new *this* is created in this function scope
that.myVar++;
console.log(that.myVar) // 1
function myFunc() {
this.myVar = 0;
setTimeout(
() => { // this taken from surrounding, meaning myFunc here
this.myVar++;
console.log(this.myVar) // 1
},
0
);
}
Useful resources
Arrow functions introduction - WesBos
JavaScript arrow function - MDN
Arrow function and lexical this
Function default parameter value
Starting from ES2015 JavaScript update, you can set default value to your function
parameters using the following syntax:
No parameter provided
undefined parameter provided
In other words, if you pass in null the default parameter won’t be applied.
Note: Default value assignment can be used with destructured parameters as well
(see next notion to see an example)
External resource
Default parameter value - ES6 Features
Default parameters - MDN
Destructuring objects and arrays
Destructuring is a convenient way of creating new variables by extracting some
values from data stored in objects or arrays.
const person = {
firstName: "Nick",
lastName: "Anderson",
age: 35,
sex: "M"
}
Without destructuring
Function parameters
Destructuring is often used to destructure objects parameters in functions.
Without destructuring
function joinFirstLastName(person) {
const firstName = person.firstName;
const lastName = person.lastName;
return firstName + '-' + lastName;
}
joinFirstLastName(person); // "Nick-Anderson"
In destructuring the object parameter person, we get a more concise function:
function joinFirstLastName({ firstName, lastName }) { // we create firstName and
lastName variables by destructuring person parameter
return firstName + '-' + lastName;
}
joinFirstLastName(person); // "Nick-Anderson"
Destructuring is even more pleasant to use with arrow functions:
joinFirstLastName(person); // "Nick-Anderson"
Array
Let’s consider the following array:
const x = myArray[0];
const y = myArray[1];
With destructuring
console.log(x) // "a"
console.log(y) // "b"
Useful resources
ES6 Features - Destructuring Assignment
Destructuring Objects - WesBos
ExploringJS - Destructuring
Array methods - map / filter / reduce / find
Map, filter, reduce and find are array methods that are coming from a programming
paradigm named functional programming.
To sum it up:
With those four methods, you can avoid the use of for and forEach loops in most
situations. When you are tempted to do a for loop, try to do it with map, filter,
reduce and find composed. You might struggle to do it at first because it requires
you to learn a new way of thinking, but once you’ve got it things get easier.
Sample code
const numbers = [0, 1, 2, 3, 4, 5, 6];
const doubledNumbers = numbers.map(n => n * 2); // [0, 2, 4, 6, 8, 10, 12]
const evenNumbers = numbers.filter(n => n % 2 === 0); // [0, 2, 4, 6]
const sum = numbers.reduce((prev, next) => prev + next, 0); // 21
const greaterThanFour = numbers.find((n) => n>4); // 5
Compute total grade sum for students with grades 10 or above by composing map,
filter and reduce:
const students = [
{ name: "Nick", grade: 10 },
{ name: "John", grade: 15 },
{ name: "Julia", grade: 19 },
{ name: "Nathalie", grade: 9 },
];
Let’s extract this function to make it more clear, just for this once:
Note: If you do not need to return a new array and just want to do a loop that has
side effects, you might just want to use a for / forEach loop instead of a map.
Array.prototype.filter()
const evenNumbers = numbers.filter(function(n) {
return n % 2 === 0; // true if "n" is par, false if "n" isn't
});
console.log(evenNumbers); // [0, 2, 4, 6]
Note : You will frequently encounter this method used in combination with arrow
functions
Array.prototype.reduce()
The reduce method goal is to reduce all elements of the array it iterates on into a
single value. How it aggregates those elements is up to you.
console.log(sum) // 21
Note : You will frequently encounter this method used in combination with arrow
functions
The second parameter is the value of the accumulator variable (acc here) at the
first iteration step (read next point to understand).
Function parameters
The function you pass as the first parameter of .reduce takes two parameters. The
first one (acc here) is the accumulator variable, whereas the second parameter (n)
is the current element.
The accumulator variable is equal to the return value of your function at the
previous iteration step. At the first step of the iteration, acc is equal to the
value you passed as .reduce second parameter.
Array.prototype.find()
const greaterThanZero = numbers.find(function(n) {
return n > 0; // return number just greater than 0 is present
});
console.log(greaterThanZero); // 1
Note : You will frequently encounter this method used in combination with arrow
functions
We are using .find on the numbers array, .find is iterating on each element of the
array and passes it to our function, until the condition is met. The goal of the
function is to return the element that satisfies the current testing function.
The .find method executes the callback function once for each index of the array
until the callback returns a truthy value.
Note : It immediately returns the value of that element (that satisfies the
condition) if found. Otherwise, returns undefined.
External Resource
Understanding map / filter / reduce in JS
Spread operator “…”
The spread operator ... has been introduced with ES2015 and is used to expand
elements of an iterable (like an array) into places where multiple elements can
fit.
Sample code
const arr1 = ["a", "b", "c"];
const arr2 = [...arr1, "d", "e", "f"]; // ["a", "b", "c", "d", "e", "f"]
function myFunc(x, y, ...params) {
console.log(x);
console.log(y);
console.log(params)
}
const n = { x, y, ...z };
console.log(n); // { x: 1, y: 2, a: 3, b: 4 }
Explanation
In iterables (like arrays)
If we have the two following arrays:
function myFunc() {
for (var i = 0; i < arguments.length; i++) {
console.log(arguments[i]);
}
}
return {
firstName: firstName,
lastName: lastName,
grades: grades,
avgGrade: avgGrade
}
}
const student = createStudent("Nick", "Anderson", 10, 12, 6);
console.log(student);
// {
// firstName: "Nick",
// lastName: "Anderson",
// grades: [10, 12, 6],
// avgGrade: 9,33
// }
Note: createStudent function is bad because we don’t check if grades.length exists
or is different from 0. But it’s easier to read this way, so I didn’t handle this
case.
const myObj = { x: 1, y: 2, a: 3, b: 4 };
const { x, y, ...z } = myObj; // object destructuring here
console.log(x); // 1
console.log(y); // 2
console.log(z); // { a: 3, b: 4 }
// z is the rest of the object destructured: myObj object minus x and y properties
destructured
const n = { x, y, ...z };
console.log(n); // { x: 1, y: 2, a: 3, b: 4 }
const x = 10;
const myObj = { x };
console.log(myObj.x) // 10
Explanation
Usually (pre-ES2015) when you declare a new object literal and want to use
variables as object properties values, you would write this kind of code:
const x = 10;
const y = 20;
const myObj = {
x: x, // assigning x variable value to myObj.x
y: y // assigning y variable value to myObj.y
};
console.log(myObj.x) // 10
console.log(myObj.y) // 20
As you can see, this is quite repetitive because the properties name of myObj are
the same as the variable names you want to assign to those properties.
With ES2015, when the variable name is the same as the property name, you can do
this shorthand:
const x = 10;
const y = 20;
const myObj = {
x,
y
};
console.log(myObj.x) // 10
console.log(myObj.y) // 20
External resources
Property shorthand - ES6 Features
Promises
A promise is an object which can be returned synchronously from an asynchronous
function (ref).
Promises can be used to avoid callback hell, and they are more and more frequently
encountered in modern JavaScript projects.
Sample code
const fetchingPosts = new Promise((res, rej) => {
$.get("/posts")
.done(posts => res(posts))
.fail(err => rej(err));
});
fetchingPosts
.then(posts => console.log(posts))
.catch(err => console.log(err));
Explanation
When you do an Ajax request the response is not synchronous because you want a
resource that takes some time to come. It even may never come if the resource you
have requested is unavailable for some reason (404).
To handle that kind of situation, ES2015 has given us promises. Promises can have
three different states:
Pending
Fulfilled
Rejected
Let’s say we want to use promises to handle an Ajax request to fetch the resource
X.
const xFetcherPromise = new Promise( // Create promise using "new" keyword and
store it into a variable
function(resolve, reject) { // Promise constructor takes a function parameter
which has resolve and reject parameters itself
$.get("X") // Launch the Ajax request
.done(function(X) { // Once the request is done...
resolve(X); // ... resolve the promise with the X value as parameter
})
.fail(function(error) { // If the request has failed...
reject(error); // ... reject the promise with the error as parameter
});
}
)
As seen in the above sample, the Promise object takes an executor function which
takes two parameters resolve and reject. Those parameters are functions which when
called are going to move the promise pending state to respectively a fulfilled and
rejected state.
The promise is in pending state after instance creation and its executor function
is executed immediately. Once one of the function resolve or reject is called in
the executor function, the promise will call its associated handlers.
xFetcherPromise
.then(function(X) {
console.log(X);
})
.catch(function(err) {
console.log(err)
})
If the promise succeeds, resolve is executed and the function passed as .then
parameter is executed.
Note : If the promise has already been fulfilled or rejected when a corresponding
handler is attached, the handler will be called, so there is no race condition
between an asynchronous operation completing and its handlers being attached. (Ref:
MDN)
External Resources
JavaScript Promises for dummies - Jecelyn Yeen
JavaScript Promise API - David Walsh
Using promises - MDN
What is a promise - Eric Elliott
JavaScript Promises: an Introduction - Jake Archibald
Promise documentation - MDN
Template literals
Template literals is an expression interpolation for single and multiple-line
strings.
In other words, it is a new string syntax in which you can conveniently use any
JavaScript expressions (variables for instance).
Sample code
const name = "Nick";
`Hello ${name}, the following expression is equal to four : ${2+2}`;
return interpolation;
}
Note : You can only name-export first-class citizens that have a name.
// mathConstants.js
export const pi = 3.14;
export const exp = 2.7;
export const alpha = 0.35;
// -------------
// myFile.js
import { pi, exp } from './mathConstants.js'; // Named import -- destructuring-like
syntax
console.log(pi) // 3.14
console.log(exp) // 2.7
// -------------
// mySecondFile.js
import * as constants from './mathConstants.js'; // Inject all exported values into
constants variable
console.log(constants.pi) // 3.14
console.log(constants.exp) // 2.7
While named imports looks like destructuring, they have a different syntax and are
not the same. They don’t support default values nor deep destructuring.
Besides, you can do aliases but the syntax is different from the one used in
destructuring:
import { foo as bar } from 'myFile.js'; // foo is imported and injected into a new
bar variable
Default import / export
Concerning the default export, there is only a single default export per module. A
default export can be a function, a class, an object or anything else. This value
is considered the “main” exported value since it will be the simplest to import.
Ref: MDN
// coolNumber.js
const ultimateNumber = 42;
export default ultimateNumber;
// ------------
// myFile.js
import number from './coolNumber.js';
// Default export, independently from its name, is automatically injected into
number variable;
console.log(number) // 42
Function exporting:
// sum.js
export default function sum(x, y) {
return x + y;
}
// -------------
// myFile.js
import sum from './sum.js';
const result = sum(1, 2);
console.log(result) // 3
External resources
ES6 Modules in bulletpoints
Export - MDN
Import - MDN
Understanding ES6 Modules
Destructuring special case - import statements
Misunderstanding ES6 Modules - Kent C. Dodds
Modules in JavaScript
JavaScript this
this operator behaves differently than in other languages and is in most cases
determined by how a function is called. (Ref: MDN).
This notion is having many subtleties and being quite hard, I highly suggest you to
deep dive in the external resources below. Thus, I will provide what I personally
have in mind to determine what this is equal to. I have learned this tip from this
article written by Yehuda Katz.
function myFunc() {
...
}
// In non-strict-mode
myFunc("hello") // window -- myFunc() is syntax sugar for myFunc.call(window,
"hello")
// In strict-mode
myFunc("hello") // undefined -- myFunc() is syntax sugar for myFunc.call(undefined,
"hello")
var person = {
myFunc: function() { ... }
}
The word class is indeed error prone if you are familiar with classes in other
languages. If you do, avoid assuming how JavaScript classes work on this basis and
consider it an entirely different notion.
Since this document is not an attempt to teach you the language from the ground up,
I will assume you know what prototypes are and how they behave. If you do not, see
the external resources listed below the sample code.
Samples
Before ES6, prototype syntax:
var Person = function(name, age) {
this.name = name;
this.age = age;
}
Person.prototype.stringSentence = function() {
return "Hello, my name is " + this.name + " and I'm " + this.age;
}
With ES6 class syntax:
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
stringSentence() {
return `Hello, my name is ${this.name} and I am ${this.age}`;
}
}
The super keyword is used to call functions on an object’s parent, including its
constructor.
super keyword must be used before the this keyword is used in constructor
Invoking super() calls the parent class constructor. If you want to pass some
arguments in a class’s constructor to its parent’s constructor, you call it with
super(arguments).
If the parent class have a method (even static) called X, you can use super.X() to
call it in a child class.
Sample Code
class Polygon {
constructor(height, width) {
this.name = 'Polygon';
this.height = height;
this.width = width;
}
getHelloPhrase() {
return `Hi, I am a ${this.name}`;
}
}
getCustomHelloPhrase() {
const polygonPhrase = super.getHelloPhrase(); // accessing parent method with
super.X() syntax
return `${polygonPhrase} with a length of ${this.length}`;
}
get area() {
return this.height * this.width;
}
}
Note 2: await must be used in an async function, which means that you can’t use
await in the top level of our code since that is not inside an async function.
Sample code
async function getGithubUser(username) { // async keyword allows usage of await in
the function and means function returns a promise
const response = await fetch(`https://api.github.com/users/${username}`); //
Execution is paused here until the Promise returned by fetch is resolved
return response.json();
}
getGithubUser('mbeaudru')
.then(user => console.log(user)) // logging user response - cannot use await
syntax since this code isn't in async function
.catch(err => console.log(err)); // if an error is thrown in our async function,
we will catch it here
Explanation with sample code
Async / Await is built on promises but they allow a more imperative style of code.
The async operator marks a function as asynchronous and will always return a
Promise. You can use the await operator in an async function to pause execution on
that line until the returned Promise from the expression either resolves or
rejects.
await operator is used to wait for a Promise to be fulfilled and can only be used
inside an async function body. When encountered, the code execution is paused until
the promise is fulfilled.
Note : fetch is a function that returns a Promise that allows to do an AJAX request
Let’s see how we could fetch a github user with promises first:
function getGithubUser(username) {
return fetch(`https://api.github.com/users/${username}`).then(response =>
response.json());
}
getGithubUser('mbeaudru')
.then(user => console.log(user))
.catch(err => console.log(err));
Here’s the async / await equivalent:
async function getGithubUser(username) { // promise + await keyword usage allowed
const response = await fetch(`https://api.github.com/users/${username}`); //
Execution stops here until fetch promise is fulfilled
return response.json();
}
getGithubUser('mbeaudru')
.then(user => console.log(user))
.catch(err => console.log(err));
async / await syntax is particularly convenient when you need to chain promises
that are interdependent.
For instance, if you need to get a token in order to be able to fetch a blog post
on a database and then the author informations:
post.author = author;
return post;
}
fetchPostById('gzIrzeo64')
.then(post => console.log(post))
.catch(err => console.log(err));
Error handling
Unless we add try / catch blocks around await expressions, uncaught exceptions –
regardless of whether they were thrown in the body of your async function or while
it’s suspended during await – will reject the promise returned by the async
function. Using the throw statement in an async function is the same as returning a
Promise that rejects. (Ref: PonyFoo).
With promises, here is how you would handle the error chain:
function getAvatarByUsername(userId) {
return getUser(userId).then(user => user.avatar);
}
function getUserAvatar(username) {
return getAvatarByUsername(username).then(avatar => ({ username, avatar }));
}
getUserAvatar('mbeaudru')
.then(res => console.log(res))
.catch(err => console.log(err)); // "User not found !"
The equivalent with async / await:
getUserAvatar('mbeaudru')
.then(res => console.log(res))
.catch(err => console.log(err)); // "User not found !"
External resources
Async/Await - JavaScript.Info
ES7 Async/Await
6 Reasons Why JavaScript’s Async/Await Blows Promises Away
JavaScript awaits
Using Async Await in Express with Node 8
Async Function
Await
Using async / await in express with node 8
Truthy / Falsy
In JavaScript, a truthy or falsy value is a value that is being casted into a
boolean when evaluated in a boolean context. An example of boolean context would be
the evaluation of an if condition:
Every value will be casted to true unless they are equal to:
false
0
"" (empty string)
null
undefined
NaN
Here are examples of boolean context:
if condition evaluation
if (myVar) {}
myVar can be any first-class citizen (variable, function, boolean) but it will be
casted into a boolean because it’s evaluated in a boolean context.
Sample code
function downToOne(n) {
const list = [];
return list;
}
downToOne(5)
//=> [ 5, 4, 3, 2, 1 ]
Catamorphisms
Catamorphisms are the opposite of Anamorphisms, in that they take objects of more
complex structure and fold them into simpler structures. Take the following example
product which take a list of integers and returns a single integer.
Sample code
function product(list) {
let product = 1;
return product;
}
product(downToOne(5)) // 120
External resources
Anamorphisms in JavaScript
Anamorphism
Catamorphism
Generators
Another way to write the downToOne function is to use a Generator. To instantiate a
Generator object, one must use the function * declaration. Generators are functions
that can be exited and later re-entered with its context (variable bindings) saved
across re-entrances.
function * downToOne(n) {
for (let i = n; i > 0; --i) {
yield i;
}
}
[...downToOne(5)] // [ 5, 4, 3, 2, 1 ]
Generators return an iterable object. When the iterator’s next() function is
called, it is executed until the first yield expression, which specifies the value
to be returned from the iterator or with yield*, which delegates to another
generator function. When a return expression is called in the generator, it will
mark the generator as done and pass back as the return value. Further calls to
next() will not return any new values.
Sample code
// Yield Example
function * idMaker() {
var index = 0;
while (index < 2) {
yield index;
index = index + 1;
}
}
gen.next().value; // 0
gen.next().value; // 1
gen.next().value; // undefined
The yield* expression enables a generator to call another generator function during
iteration.
// Yield * Example
function * genB(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}
function * genA(i) {
yield i;
yield* genB(i);
yield i + 10;
}
gen.next().value; // 10
gen.next().value; // 11
gen.next().value; // 12
gen.next().value; // 13
gen.next().value; // 20
// Generator Return Example
function* yieldAndReturn() {
yield "Y";
return "R";
yield "unreachable";
}
Sample code
class Repo {
static getName() {
return "Repo name is modern-js-cheatsheet"
}
}
// Note that we did not have to create an instance of the Repo class
console.log(Repo.getName()) // Repo name is modern-js-cheatsheet
class Repo {
static getName() {
return "Repo name is modern-js-cheatsheet"
}
static modifyName() {
return this.getName() + '-added-this'
}
}
class Repo {
static getName() {
return "Repo name is modern-js-cheatsheet"
}
useName() {
return Repo.getName() + ' and it contains some really important stuff'
}
}
class Repo {
static getName() {
return "Repo name is modern-js-cheatsheet"
}
useName() {
// Calls the static method as a property of the constructor
return this.constructor.getName() + ' and it contains some really important
stuff'
}
}
Source: MDN
Variable mutation
A variable is said to have been mutated when its initial value has changed
afterward.