Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Cheatsheet for the JavaScript knowledge you will frequently encounter in modern projects.

License

NotificationsYou must be signed in to change notification settings

mixedmuddler/modern-js-cheatsheet

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 

Repository files navigation

Modern JavaScript cheatsheetImage Credits:Ahmad Awais ⚡️

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 updatehere; it's very well done.

Complementary Resources

When you struggle to understand a notion, I suggest you look for answers on the following resources:

Table of Contents

Notions

Variable declaration: var, const, let

In JavaScript, there are three keywords available to declare a variable, and each has its differences. Those arevar,let andconst.

Short explanation

Variables declared withconst keyword can't be reassigned, whilelet andvar can.

I recommend always declaring your variables withconst by default, and withlet if you need tomutate it or reassign it later.

ScopeReassignableMutableTemporal Dead Zone
constBlockNoYesYes
letBlockYesYesYes
varFunctionYesYesNo

Sample code

constperson="Nick";person="John"// Will raise an error, person can't be reassigned
letperson="Nick";person="John";console.log(person)// "John", reassignment is allowed with let

Detailed explanation

Thescope of a variable roughly means "where is this variable available in the code".

var

var declared variables arefunction scoped, meaning that when a variable is created in a function, everything in that function can access that variable. Besides, afunction scoped variable created in a function can't be accessed outside this function.

I recommend you to picture it as if anX scoped variable meant that this variable was a property of X.

functionmyFunction(){varmyVar="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:

functionmyFunction(){varmyVar="Nick";if(true){varmyVar="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 callvar hoisting.

This portion of code:

console.log(myVar)// undefined -- no error raisedvarmyVar=2;

is understood at execution like:

varmyVar;console.log(myVar)// undefined -- no error raisedmyVar=2;
let

var andlet are about the same, butlet declared variables

  • areblock scoped
  • arenot accessible before they are assigned
  • can't be re-declared in the same scope

Let's see the impact of block-scoping taking our previous example:

functionmyFunction(){letmyVar="Nick";if(true){letmyVar="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 forlet (andconst) variables for not being accessible before being assigned:

console.log(myVar)// raises a ReferenceError !letmyVar=2;

By contrast withvar variables, if you try to read or write on alet orconst variable before they are assigned an error will be raised. This phenomenon is often calledTemporal dead zone orTDZ.

Note: Technically,let andconst 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 thisvery detailed explanation here if you want to know more.

In addition, you can't re-declare alet variable:

letmyVar=2;letmyVar=3;// Raises a SyntaxError
const

const declared variables behave likelet variables, but also they can't be reassigned.

To sum it up,const variables:

  • areblock scoped
  • are not accessible before being assigned
  • can't be re-declared in the same scope
  • can't be reassigned
constmyVar="Nick";myVar="John"// raises an error, reassignment is not allowed
constmyVar="Nick";constmyVar="John"// raises an error, re-declaration is not allowed

But there is a subtlety :const variables are notimmutable ! Concretely, it means thatobject andarrayconst declared variablescan be mutated.

For objects:

constperson={name:'Nick'};person.name='John'// this will work ! person variable is not completely reassigned, but mutatedconsole.log(person.name)// "John"person="Sandra"// raises an error, because reassignment is not allowed with const declared variables

For arrays:

constperson=[];person.push('John');// this will work ! person variable is not completely reassigned, but mutatedconsole.log(person[0])// "John"person=["Nick"]// raises an error, because reassignment is not allowed with const declared variables

External resource

Arrow function

The ES6 JavaScript update has introducedarrow functions, which is another way to declare and use functions. Here are the benefits they bring:

  • More concise
  • this is picked up from surroundings
  • implicit return

Sample code

  • Concision and implicit return
functiondouble(x){returnx*2;}// Traditional wayconsole.log(double(2))// 4
constdouble=x=>x*2;// Same function written as an arrow function with implicit returnconsole.log(double(2))// 4
  • this reference

In an arrow function,this is equal to thethis 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.

functionmyFunc(){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:

  • Implicit VS Explicit return

Anexplicit return is a function where thereturn keyword is used in its body.

functiondouble(x){returnx*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 doimplicit return which means that you don't need to use the keywordreturn to return a value.

constdouble=(x)=>{returnx*2;// Explicit return here}

Since this function only returns something (no instructions before thereturn keyword) we can do an implicit return.

constdouble=(x)=>x*2;// Correct, returns x*2

To do so, we only need toremove the brackets and thereturn keyword. That's why it's called animplicit return, thereturn keyword is not there, but this function will indeed returnx * 2.

Note: If your function does not return a value (withside effects), it doesn't do an explicit nor an implicit return.

Besides, if you want to implicitly return anobject youmust have parentheses around it since it will conflict with the block braces:

constgetPerson=()=>({name:"Nick",age:24})console.log(getPerson())// { name: "Nick", age: 24 } -- object implicitly returned by arrow function
  • Only one argument

If your function only takes one parameter, you can omit the parentheses around it. If we take back the abovedouble code:

constdouble=(x)=>x*2;// this arrow function only takes one parameter

Parentheses around the parameter can be avoided:

constdouble=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.

()=>{// parentheses are provided, everything is fineconstx=2;returnx;}
=>{// No parentheses, this won't work!constx=2;returnx;}
this reference

To understand this subtlety introduced with arrow functions, you must know howthis behaves in JavaScript.

In an arrow function,this is equal to thethis value of the enclosing execution context. What it means is that an arrow function doesn't create a newthis, it grabs it from its surrounding instead.

Without arrow function, if you wanted to access a variable fromthis in a function inside a function, you had to use thethat = this orself = this trick.

For instance, using setTimeout function inside myFunc:

functionmyFunc(){this.myVar=0;varthat=this;// that = this tricksetTimeout(function(){// A new *this* is created in this function scopethat.myVar++;console.log(that.myVar)// 1console.log(this.myVar)// undefined -- see function declaration above},0);}

But with arrow function,this is taken from its surrounding:

functionmyFunc(){this.myVar=0;setTimeout(()=>{// this taken from surrounding, meaning myFunc herethis.myVar++;console.log(this.myVar)// 1},0);}

Useful resources

Function default parameter value

Starting from ES2015 JavaScript update, you can set default value to your function parameters using the following syntax:

functionmyFunc(x=10){returnx;}console.log(myFunc())// 10 -- no value is provided so x default value 10 is assigned to x in myFuncconsole.log(myFunc(5))// 5 -- a value is provided so x is equal to 5 in myFuncconsole.log(myFunc(undefined))// 10 -- undefined value is provided so default value is assigned to xconsole.log(myFunc(null))// null -- a value (null) is provided, see below for more details

The default parameter is applied in two and only two situations:

  • No parameter provided
  • undefined parameter provided

In other words, if you pass innull the default parameterwon't be applied.

Note: Default value assignment can be used with destructured parameters as well (see next notion to see an example)

External resource

Destructuring objects and arrays

Destructuring is a convenient way of creating new variables by extracting some values from data stored in objects or arrays.

To name a few use cases,destructuring can be used to destructure function parameters orthis.props in React projects for instance.

Explanation with sample code

  • Object

Let's consider the following object for all the samples:

constperson={firstName:"Nick",lastName:"Anderson",age:35,sex:"M"}

Without destructuring

constfirst=person.firstName;constage=person.age;constcity=person.city||"Paris";

With destructuring, all in one line:

const{firstName:first, age, city="Paris"}=person;// That's it !console.log(age)// 35 -- A new variable age is created and is equal to person.ageconsole.log(first)// "Nick" -- A new variable first is created and is equal to person.firstNameconsole.log(firstName)// ReferenceError -- person.firstName exists BUT the new variable created is named firstconsole.log(city)// "Paris" -- A new variable city is created and since person.city is undefined, city is equal to the default value provided "Paris".

Note : Inconst { age } = person;, the brackets afterconst keyword are not used to declare an object nor a block but is thedestructuring syntax.

  • Function parameters

Destructuring is often used to destructure objects parameters in functions.

Without destructuring

functionjoinFirstLastName(person){constfirstName=person.firstName;constlastName=person.lastName;returnfirstName+'-'+lastName;}joinFirstLastName(person);// "Nick-Anderson"

In destructuring the object parameterperson, we get a more concise function:

functionjoinFirstLastName({ firstName, lastName}){// we create firstName and lastName variables by destructuring person parameterreturnfirstName+'-'+lastName;}joinFirstLastName(person);// "Nick-Anderson"

Destructuring is even more pleasant to use witharrow functions:

constjoinFirstLastName=({ firstName, lastName})=>firstName+'-'+lastName;joinFirstLastName(person);// "Nick-Anderson"
  • Array

Let's consider the following array:

constmyArray=["a","b","c"];

Without destructuring

constx=myArray[0];consty=myArray[1];

With destructuring

const[x,y]=myArray;// That's it !console.log(x)// "a"console.log(y)// "b"

Useful resources

Array methods - map / filter / reduce

Map,filter andreduce are array methods that are coming from a programming paradigm namedfunctional programming.

To sum it up:

  • Array.prototype.map() takes an array, does something on its elements and returns an array with the transformed elements.
  • Array.prototype.filter() takes an array, decides element by element if it should keep it or not and returns an array with the kept elements only
  • Array.prototype.reduce() takes an array and aggregates the elements into a single value (which is returned)

I recommend to use them as much as possible in following the principles of functional programming because they are composable, concise and elegant.

With those three methods, you can avoid the use offor andforEach loops in most situations. When you are tempted to do afor loop, try to do it withmap,filter andreduce 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

constnumbers=[0,1,2,3,4,5,6];constdoubledNumbers=numbers.map(n=>n*2);// [0, 2, 4, 6, 8, 10, 12]constevenNumbers=numbers.filter(n=>n%2===0);// [0, 2, 4, 6]constsum=numbers.reduce((prev,next)=>prev+next,0);// 21

Compute total grade sum for students with grades 10 or above by composing map, filter and reduce:

conststudents=[{name:"Nick",grade:10},{name:"John",grade:15},{name:"Julia",grade:19},{name:"Nathalie",grade:9},];constaboveTenSum=students.map(student=>student.grade)// we map the students array to an array of their grades.filter(grade=>grade>=10)// we filter the grades array to keep those 10 or above.reduce((prev,next)=>prev+next,0);// we sum all the grades 10 or above one by oneconsole.log(aboveTenSum)// 44 -- 10 (Nick) + 15 (John) + 19 (Julia), Nathalie below 10 is ignored

Explanation

Let's consider the following array of numbers for our examples:

constnumbers=[0,1,2,3,4,5,6];
Array.prototype.map()
constdoubledNumbers=numbers.map(function(n){returnn*2;});console.log(doubledNumbers);// [0, 2, 4, 6, 8, 10, 12]

What's happening here? We are using .map on thenumbers array, the map is iterating on each element of the array and passes it to our function. The goal of the function is to produce and return a new value from the one passed so that map can replace it.

Let's extract this function to make it more clear, just for this once:

constdoubleN=function(n){returnn*2;};constdoubledNumbers=numbers.map(doubleN);console.log(doubledNumbers);// [0, 2, 4, 6, 8, 10, 12]

Note : You will frequently encounter this method used in combination witharrow functions

constdoubledNumbers=numbers.map(n=>n*2);console.log(doubledNumbers);// [0, 2, 4, 6, 8, 10, 12]

numbers.map(doubleN) produces[doubleN(0), doubleN(1), doubleN(2), doubleN(3), doubleN(4), doubleN(5), doubleN(6)] which is equal to[0, 2, 4, 6, 8, 10, 12].

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()
constevenNumbers=numbers.filter(function(n){returnn%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 witharrow functions

constevenNumbers=numbers.filter(n=>n%2===0);console.log(evenNumbers);// [0, 2, 4, 6]

We are using .filter on thenumbers array, filter is iterating on each element of the array and passes it to our function. The goal of the function is to return a boolean that will determine whether the current value will be kept or not. Filter then returns the array with only the kept values.

Array.prototype.reduce()

The reduce method goal is toreduce all elements of the array it iterates on into a single value. How it aggregates those elements is up to you.

constsum=numbers.reduce(function(acc,n){returnacc+n;},0// accumulator variable value at first iteration step);console.log(sum)// 21

Note : You will frequently encounter this method used in combination witharrow functions

constsum=numbers.reduce((acc,n)=>acc+n,0);console.log(sum)// 21

Just like for .map and .filter methods, .reduce is applied on an array and takes a function as the first parameter.

This time though, there are changes:

  • .reduce takes two parameters

The first parameter is a function that will be called at each iteration step.

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 theprevious iteration step. At the first step of the iteration,acc is equal to the value you passed as .reduce second parameter.

At first iteration step

acc = 0 because we passed in 0 as the second parameter for reduce

n = 0 first element of thenumber array

Function returnsacc +n --> 0 + 0 --> 0

At second iteration step

acc = 0 because it's the value the function returned at the previous iteration step

n = 1 second element of thenumber array

Function returnsacc +n --> 0 + 1 --> 1

At third iteration step

acc = 1 because it's the value the function returned at the previous iteration step

n = 2 third element of thenumber array

Function returnsacc +n --> 1 + 2 --> 3

At fourth iteration step

acc = 3 because it's the value the function returned at the previous iteration step

n = 3 fourth element of thenumber array

Function returnsacc +n --> 3 + 3 --> 6

[...] At last iteration step

acc = 15 because it's the value the function returned at the previous iteration step

n = 6 last element of thenumber array

Function returnsacc +n --> 15 + 6 --> 21

As it is the last iteration step,.reduce returns 21.

External Resource

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

constarr1=["a","b","c"];constarr2=[...arr1,"d","e","f"];// ["a", "b", "c", "d", "e", "f"]
functionmyFunc(x,y, ...params){console.log(x);console.log(y);console.log(params)}myFunc("a","b","c","d","e","f")// "a"// "b"// ["c", "d", "e", "f"]
const{ x, y, ...z}={x:1,y:2,a:3,b:4};console.log(x);// 1console.log(y);// 2console.log(z);// { a: 3, b: 4 }constn={ 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:

constarr1=["a","b","c"];constarr2=[arr1,"d","e","f"];// [["a", "b", "c"], "d", "e", "f"]

arr2 the first element is an array becausearr1 is injected as is intoarr2. But what we want isarr2 to be an array of letters. To do so, we canspread the elements ofarr1 intoarr2.

With spread operator

constarr1=["a","b","c"];constarr2=[...arr1,"d","e","f"];// ["a", "b", "c", "d", "e", "f"]
Function rest parameter

In function parameters, we can use the rest operator to inject parameters into an array we can loop in. There is already anarguments object bound to every function that is equal to an array of all the parameters passed into the function.

functionmyFunc(){for(vari=0;i<arguments.length;i++){console.log(arguments[i]);}}myFunc("Nick","Anderson",10,12,6);// "Nick"// "Anderson"// 10// 12// 6

But let's say that we want this function to create a new student with its grades and with its average grade. Wouldn't it be more convenient to extract the first two parameters into two separate variables, and then have all the grades in an array we can iterate over?

That's exactly what the rest operator allows us to do!

functioncreateStudent(firstName,lastName, ...grades){// firstName = "Nick"// lastName = "Anderson"// [10, 12, 6] -- "..." takes all other parameters passed and creates a "grades" array variable that contains themconstavgGrade=grades.reduce((acc,curr)=>acc+curr,0)/grades.length;// computes average grade from gradesreturn{firstName:firstName,lastName:lastName,grades:grades,avgGrade:avgGrade}}conststudent=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.

Object properties spreading

For this one, I recommend you read previous explanations about the rest operator on iterables and function parameters.

constmyObj={x:1,y:2,a:3,b:4};const{ x, y, ...z}=myObj;// object destructuring hereconsole.log(x);// 1console.log(y);// 2console.log(z);// { a: 3, b: 4 }// z is the rest of the object destructured: myObj object minus x and y properties destructuredconstn={ x, y, ...z};console.log(n);// { x: 1, y: 2, a: 3, b: 4 }// Here z object properties are spread into n

External resources

Object property shorthand

When assigning a variable to an object property, if the variable name is equal to the property name, you can do the following:

constx=10;constmyObj={ x};console.log(myObj.x)// 10

Explanation

Usually (pre-ES2015) when you declare a newobject literal and want to use variables as object properties values, you would write this kind of code:

constx=10;consty=20;constmyObj={x:x,// assigning x variable value to myObj.xy:y// assigning y variable value to myObj.y};console.log(myObj.x)// 10console.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:

constx=10;consty=20;constmyObj={  x,  y};console.log(myObj.x)// 10console.log(myObj.y)// 20

External resources

Promises

A promise is an object which can be returned synchronously from an asynchronous function (ref).

Promises can be used to avoidcallback hell, and they are more and more frequently encountered in modern JavaScript projects.

Sample code

constfetchingPosts=newPromise((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 anAjax 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 uspromises. 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.

Create the promise

We firstly are going to create a promise. We will use the jQuery get method to do our Ajax request to X.

constxFetcherPromise=newPromise(// Create promise using "new" keyword and store it into a variablefunction(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 anexecutor function which takes two parametersresolve andreject. Those parameters are functions which when called are going to move the promisepending state to respectively afulfilled andrejected state.

The promise is in pending state after instance creation and itsexecutor function is executed immediately. Once one of the functionresolve orreject is called in theexecutor function, the promise will call its associated handlers.

Promise handlers usage

To get the promise result (or error), we must attach to it handlers by doing the following:

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.

If it fails,reject is executed and the function passed as.catch 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

Template literals

Template literals is anexpression 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

constname="Nick";`Hello${name}, the following expression is equal to four :${2+2}`;// Hello Nick, the following expression is equal to four: 4

External resources

Tagged template literals

Template tags arefunctions that can be prefixed to atemplate literal. When a function is called this way, the first parameter is an array of thestrings that appear between the template's interpolated variables, and the subsequent parameters are the interpolated values. Use a spread operator... to capture all of them.(Ref: MDN).

Note : A famous library namedstyled-components heavily relies on this feature.

Below is a toy example on how they work.

functionhighlight(strings, ...values){constinterpolation=strings.reduce((prev,current)=>{returnprev+current+(values.length ?"<mark>"+values.shift()+"</mark>" :"");},"");returninterpolation;}constcondiment="jam";constmeal="toast";highlight`I like${condiment} on${meal}.`;// "I like <mark>jam</mark> on <mark>toast</mark>."

A more interesting example:

functioncomma(strings, ...values){returnstrings.reduce((prev,next)=>{letvalue=values.shift()||[];value=value.join(", ");returnprev+next+value;},"");}constsnacks=['apples','bananas','cherries'];comma`I like${snacks} to snack on.`;// "I like apples, bananas, cherries to snack on."

External resources

Imports / Exports

ES6 modules are used to access variables or functions in a module explicitly exported by the modules it imports.

I highly recommend to take a look at MDN resources on import/export (see external resources below), it is both straightforward and complete.

Explanation with sample code

Named exports

Named exports are used to export several values from a module.

Note : You can only name-exportfirst-class citizens that have a name.

// mathConstants.jsexportconstpi=3.14;exportconstexp=2.7;exportconstalpha=0.35;// -------------// myFile.jsimport{pi,exp}from'./mathConstants.js';// Named import -- destructuring-like syntaxconsole.log(pi)// 3.14console.log(exp)// 2.7// -------------// mySecondFile.jsimport*asconstantsfrom'./mathConstants.js';// Inject all exported values into constants variableconsole.log(constants.pi)// 3.14console.log(constants.exp)// 2.7

While named imports looks likedestructuring, they have a different syntax and are not the same. They don't support default values nordeep destructuring.

Besides, you can do aliases but the syntax is different from the one used in destructuring:

import{fooasbar}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.jsconstultimateNumber=42;exportdefaultultimateNumber;// ------------// myFile.jsimportnumberfrom'./coolNumber.js';// Default export, independently from its name, is automatically injected into number variable;console.log(number)// 42

Function exporting:

// sum.jsexportdefaultfunctionsum(x,y){returnx+y;}// -------------// myFile.jsimportsumfrom'./sum.js';constresult=sum(1,2);console.log(result)// 3

External resources

JavaScriptthis

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 whatthis is equal to. I have learned this tip fromthis article written by Yehuda Katz.

functionmyFunc(){  ...}// After each statement, you find the value of *this* in myFuncmyFunc.call("myString","hello")// "myString" -- first .call parameter value is injected into *this*// In non-strict-modemyFunc("hello")// window -- myFunc() is syntax sugar for myFunc.call(window, "hello")// In strict-modemyFunc("hello")// undefined -- myFunc() is syntax sugar for myFunc.call(undefined, "hello")
varperson={myFunc:function(){ ...}}person.myFunc.call(person,"test")// person Object -- first call parameter is injected into *this*person.myFunc("test")// person Object -- person.myFunc() is syntax sugar for person.myFunc.call(person, "test")varmyBoundFunc=person.myFunc.bind("hello")// Creates a new function in which we inject "hello" in *this* valueperson.myFunc("test")// person Object -- The bind method has no effect on the original methodmyBoundFunc("test")// "hello" -- myBoundFunc is person.myFunc with "hello" bound to *this*

External resources

Class

JavaScript is aprototype-based language (whereas Java isclass-based language, for instance). ES6 has introduced JavaScript classes which are meant to be a syntactic sugar for prototype-based inheritance andnot a new class-based inheritance model (ref).

The wordclass 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 believe 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:

varPerson=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:

classPerson{constructor(name,age){this.name=name;this.age=age;}stringSentence(){return`Hello, my name is${this.name} and I am${this.age}`;}}constmyPerson=newPerson("Manu",23);console.log(myPerson.age)// 23console.log(myPerson.stringSentence())// "Hello, my name is Manu and I'm 23

External resources

For prototype understanding:

For classes understanding:

Extends andsuper keywords

Theextends keyword is used in class declarations or class expressions to create a class which is a child of another class (Ref: MDN). The subclass inherits all the properties of the superclass and additionally can add new properties or modify the inherited ones.

Thesuper keyword is used to call functions on an object's parent, including its constructor.

  • super keyword must be used before thethis keyword is used in constructor
  • Invokingsuper() 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 withsuper(arguments).
  • If the parent class have a method (even static) calledX, you can usesuper.X() to call it in a child class.

Sample Code

classPolygon{constructor(height,width){this.name='Polygon';this.height=height;this.width=width;}getHelloPhrase(){return`Hi, I am a${this.name}`;}}classSquareextendsPolygon{constructor(length){// Here, it calls the parent class' constructor with lengths// provided for the Polygon's width and heightsuper(length,length);// Note: In derived classes, super() must be called before you// can use 'this'. Leaving this out will cause a reference error.this.name='Square';this.length=length;}getCustomHelloPhrase(){constpolygonPhrase=super.getHelloPhrase();// accessing parent method with super.X() syntaxreturn`${polygonPhrase} with a length of${this.length}`;}getarea(){returnthis.height*this.width;}}constmySquare=newSquare(10);console.log(mySquare.area)// 100console.log(mySquare.getHelloPhrase())// 'Hi, I am a Square' -- Square inherits from Polygon and has access to its methodsconsole.log(mySquare.getCustomHelloPhrase())// 'Hi, I am a Square with a length of 10'

Note : If we had tried to usethis before callingsuper() in Square class, a ReferenceError would have been raised:

classSquareextendsPolygon{constructor(length){this.height;// ReferenceError, super needs to be called first!// Here, it calls the parent class' constructor with lengths// provided for the Polygon's width and heightsuper(length,length);// Note: In derived classes, super() must be called before you// can use 'this'. Leaving this out will cause a reference error.this.name='Square';}}

External Resources

Async Await

In addition toPromises, there is a new syntax you might encounter to handle asynchronous code namedasync / await.

The purpose of async/await functions is to simplify the behavior of using promises synchronously and to perform some behavior on a group of Promises. Just as Promises are similar to structured callbacks, async/await is similar to combining generators and promises. Async functionsalways return a Promise. (Ref: MDN)

Note : You must understand what promises are and how they work before trying to understand async / await since they rely on it.

Note 2:await must be used in anasync 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

asyncfunctiongetGithubUser(username){// async keyword allows usage of await in the function and means function returns a promiseconstresponse=awaitfetch(`https://api.github.com/users/${username}`);// Execution is paused here until the Promise returned by fetch is resolvedreturnresponse.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.

Theasync operator marks a function as asynchronous and will always return aPromise. You can use theawait operator in anasync function to pause execution on that line until the returned Promise from the expression either resolves or rejects.

asyncfunctionmyFunc(){// we can use await operator because this function is asyncreturn"hello world";}myFunc().then(msg=>console.log(msg))// "hello world" -- myFunc's return value is turned into a promise because of async operator

When thereturn statement of an async function is reached, the Promise is fulfilled with the value returned. If an error is thrown inside an async function, the Promise state will turn torejected. If no value is returned from an async function, a Promise is still returned and resolves with no value when execution of the async function is complete.

await operator is used to wait for aPromise to be fulfilled and can only be used inside anasync 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:

functiongetGithubUser(username){returnfetch(`https://api.github.com/users/${username}`).then(response=>response.json());}getGithubUser('mbeaudru').then(user=>console.log(user)).catch(err=>console.log(err));

Here's theasync / await equivalent:

asyncfunctiongetGithubUser(username){// promise + await keyword usage allowedconstresponse=awaitfetch(`https://api.github.com/users/${username}`);// Execution stops here until fetch promise is fulfilledreturnresponse.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:

Note :await expressions needs to be wrapped in parentheses to call its resolved value's methods and properties on the same line.

asyncfunctionfetchPostById(postId){consttoken=(awaitfetch('token_url')).json().token;constpost=(awaitfetch(`/posts/${postId}?token=${token}`)).json();constauthor=(awaitfetch(`/users/${post.authorId}`)).json();post.author=author;returnpost;}fetchPostById('gzIrzeo64').then(post=>console.log(post)).catch(err=>console.log(err));
Error handling

Unless we addtry / catch blocks aroundawait expressions, uncaught exceptions – regardless of whether they were thrown in the body of yourasync function or while it’s suspended duringawait – will reject the promise returned by theasync function. Using thethrow statement in an async function is the same as returning a Promise that rejects.(Ref: PonyFoo).

Note : Promises behave the same!

With promises, here is how you would handle the error chain:

functiongetUser(){// This promise will be rejected!returnnewPromise((res,rej)=>rej("User not found !"));}functiongetAvatarByUsername(userId){returngetUser(userId).then(user=>user.avatar);}functiongetUserAvatar(username){returngetAvatarByUsername(username).then(avatar=>({ username, avatar}));}getUserAvatar('mbeaudru').then(res=>console.log(res)).catch(err=>console.log(err));// "User not found !"

The equivalent withasync / await:

asyncfunctiongetUser(){// The returned promise will be rejected!throw"User not found !";}asyncfunctiongetAvatarByUsername(userId)=>{constuser=awaitgetUser(userId);returnuser.avatar;}asyncfunctiongetUserAvatar(username){varavatar=awaitgetAvatarByUsername(username);return{ username, avatar};}getUserAvatar('mbeaudru').then(res=>console.log(res)).catch(err=>console.log(err));// "User not found !"

External resources

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 anif condition:

Every value will be casted totrue unless they are equal to:

  • false
  • 0
  • "" (empty string)
  • null
  • undefined
  • NaN

Here are examples ofboolean context:

  • if condition evaluation
if(myVar){}

myVar can be anyfirst-class citizen (variable, function, boolean) but it will be casted into a boolean because it's evaluated in a boolean context.

  • After logicalNOT! operator

This operator returns false if its single operand can be converted to true; otherwise, returns true.

!0// true -- 0 is falsy so it returns true!!0// false -- 0 is falsy so !0 returns true so !(!0) returns false!!""// false -- empty string is falsy so NOT (NOT false) equals false
  • With theBoolean object constructor
newBoolean(0)// falsenewBoolean(1)// true
  • In a ternary evaluation
myVar ?"truthy" :"falsy"

myVar is evaluated in a boolean context.

Be careful when comparing 2 values. The object values (that should be cast to true) isnot being casted to Boolean but it forced to convert into a primitive value one usingToPrimitives specification. Internally, when an object is compared to Boolean value like[] == true, it does[].toString() == true so...

leta=[]==true// a is false since [].toString() give "" back.letb=[1]==true// b is true since [1].toString() give "1" back.letc=[2]==true// c is false since [2].toString() give "2" back.

External resources

Anamorphisms and Catamorphisms

Anamorphisms

Anamorphisms are functions that map from some object to a more complex structure containing the type of the object. It is the process ofunfolding a simple structure into a more complex one. Consider unfolding an integer to a list of integers. The integer is our initial object and the list of integers is the more complex structure.

Sample code

functiondownToOne(n){constlist=[];for(leti=n;i>0;--i){list.push(i);}returnlist;}downToOne(5)//=> [ 5, 4, 3, 2, 1 ]

Catamorphisms

Catamorphisms are the opposite of Anamorphisms, in that they take objects of more complex structure andfold them into simpler structures. Take the following exampleproduct which take a list of integers and returns a single integer.

Sample code

functionproduct(list){letproduct=1;for(constnoflist){product=product*n;}returnproduct;}product(downToOne(5))// 120

External resources

Generators

Another way to write thedownToOne function is to use a Generator. To instantiate aGenerator object, one must use thefunction * declaration. Generators are functions that can be exited and later re-entered with its context (variable bindings) saved across re-entrances.

For example, thedownToOne function above can be rewritten as:

function*downToOne(n){for(leti=n;i>0;--i){yieldi;}}[...downToOne(5)]// [ 5, 4, 3, 2, 1 ]

Generators return an iterable object. When the iterator'snext() function is called, it is executed until the firstyield expression, which specifies the value to be returned from the iterator or withyield*, which delegates to another generator function. When areturn expression is called in the generator, it will mark the generator as done and pass back as the return value. Further calls tonext() will not return any new values.

Sample code

// Yield Examplefunction*idMaker(){varindex=0;while(index<2){yieldindex;index=index+1;}}vargen=idMaker();gen.next().value;// 0gen.next().value;// 1gen.next().value;// undefined

Theyield* expression enables a generator to call another generator function during iteration.

// Yield * Examplefunction*genB(i){yieldi+1;yieldi+2;yieldi+3;}function*genA(i){yieldi;yield*genB(i);yieldi+10;}vargen=genA(10);gen.next().value;// 10gen.next().value;// 11gen.next().value;// 12gen.next().value;// 13gen.next().value;// 20
// Generator Return Examplefunction*yieldAndReturn(){yield"Y";return"R";yield"unreachable";}vargen=yieldAndReturn()gen.next();// { value: "Y", done: false }gen.next();// { value: "R", done: true }gen.next();// { value: undefined, done: true }

External resources

Static Methods

Short explanation

Thestatic keyword is used in classes to declare static methods. Static methods are functions in a class that belongs to the class object and are not available to any instance of that class.

Sample code

classRepo{staticgetName(){return"Repo name is modern-js-cheatsheet"}}// Note that we did not have to create an instance of the Repo classconsole.log(Repo.getName())// Repo name is modern-js-cheatsheetletr=newRepo();console.log(r.getName())// Uncaught TypeError: r.getName is not a function

Detailed explanation

Static methods can be called within another static method by using thethis keyword, this doesn't work for non-static methods though. Non-static methods cannot directly access static methods using thethis keyword.

Calling other static methods from a static method.

To call a static method from another static method, thethis keyword can be used like so;

classRepo{staticgetName(){return"Repo name is modern-js-cheatsheet"}staticmodifyName(){returnthis.getName()+'-added-this'}}console.log(Repo.modifyName())// Repo name is modern-js-cheatsheet-added-this
Calling static methods from non-static methods.

Non-static methods can call static methods in 2 ways;

  1. Using the class name.

To get access to a static method from a non-static method we use the class name and call the static method like a property. e.gClassName.StaticMethodName

classRepo{staticgetName(){return"Repo name is modern-js-cheatsheet"}useName(){returnRepo.getName()+' and it contains some really important stuff'}}// we need to instantiate the class to use non-static methodsletr=newRepo()console.log(r.useName())// Repo name is modern-js-cheatsheet and it contains some really important stuff
  1. Using the constructor

Static methods can be called as properties on the constructor object.

classRepo{staticgetName(){return"Repo name is modern-js-cheatsheet"}useName(){// Calls the static method as a property of the constructorreturnthis.constructor.getName()+' and it contains some really important stuff'}}// we need to instantiate the class to use non-static methodsletr=newRepo()console.log(r.useName())// Repo name is modern-js-cheatsheet and it contains some really important stuff

External resources

Glossary

Scope

The context in which values and expressions are "visible," or can be referenced. If a variable or other expression is not "in the current scope," then it is unavailable for use.

Source:MDN

Variable mutation

A variable is said to have been mutated when its initial value has changed afterward.

varmyArray=[];myArray.push("firstEl")// myArray is being mutated

A variable is said to beimmutable if it can't be mutated.

Check MDN Mutable article for more details.

About

Cheatsheet for the JavaScript knowledge you will frequently encounter in modern projects.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

[8]ページ先頭

©2009-2025 Movatter.jp