Javascript Fundamentals
Javascript Fundamentals
of Contents
Preface
Introduction to JavaScript
ECMAScript
Lexical Structure
Variables
Types
Expressions
Prototypal inheritance
Classes
Exceptions
Semicolons
Quotes
Functions
Arrow Functions
Closures
Arrays
Loops
Events
The Event Loop
Asynchronous programming and callbacks
Promises
Template Literals
The Set Data Structure
The Map Data Structure
Loops and Scope
Timers
this in JavaScript
JavaScript Strict Mode
JavaScript Immediately-invoked Function Expressions (IIFE)
Async and Await
2
Dates in JavaScript
Math operators
The Math object
Introduction to Unicode and UTF-8
Unicode in JavaScript
Functional Programming
Regular Expressions
ES Modules
CommonJS
Glossary
3
Preface
Preface
Welcome!
Thank you for getting this ebook.
I hope its content will help you achieve what you want.
Flavio
My website is flaviocopes.com.
4
Introduction to JavaScript
Introduction to JavaScript
JavaScript is one of the most popular programming languages in the world,
and now widely used also outside of the browser. The rise of Node.js in the
last few years unlocked backend development, once the domain of Java,
Ruby, Python and PHP and more traditional server-side languages. Learn all
about it!
Introduction
Basic definition of JavaScript
JavaScript versions
Introduction
JavaScript is one of the most popular programming languages in the world.
Created in 20 years ago, it's gone a very long way since its humble beginnings.
Being the first - and the only - scripting language that was supported natively by web
browsers, it simply sticked.
In the beginnings, it was not nearly powerful as it is today, and it was mainly used for fancy
animations and the marvel known at the time as DHTML.
With the growing needs that the web platform demands, JavaScript had the responsibility to
grow as well, to accommodate the needs of one of the most widely used ecosystem of the
world.
Many things were introduced in the platform, with browser APIs, but the language grew quite a
lot as well.
JavaScript is now widely used also outside of the browser. The rise of Node.js in the last few
years unlocked backend development, once the domain of Java, Ruby, Python and PHP and
more traditional server-side languages.
JavaScript is now also the language powering databases and many more applications, and it's
even possible to develop embedded applications, mobile apps, TV sets apps and much more.
What started as a tiny language inside the browser is now the most popular language in the
world.
5
Introduction to JavaScript
high level: it provides abstractions that allow you to ignore the details of the machine
where it's running on. It manages memory automatically with a garbage collector, so you
can focus on the code instead of managing memory locations, and provides many
constructs which allow you to deal with highly powerful variables and objects.
dynamic: opposed to static programming languages, a dynamic language executes at
runtime many of the things that a static language does at compile time. This has pros and
cons, and it gives us powerful features like dynamic typing, late binding, reflection,
functional programming, object runtime alteration, closures and much more.
dynamically typed: a variable does not enforce a type. You can reassign any type to a
variable, for example assigning an integer to a variable that holds a string.
weakly typed: as opposed to strong typing, weakly (or loosely) typed languages do not
enforce the type of an object, allowing more flexibility but denying us type safety and type
checking (something that TypeScript and Flow aim to improve)
interpreted: it's commonly known as an interpreted language, which means that it does
not need a compilation stage before a program can run, as opposed to C, Java or Go for
example. In practice, browsers do compile JavaScript before executing it, for performance
reasons, but this is transparent to you: there is no additional step involved.
multi-paradigm: the language does not enforce any particular programming paradigm,
unlike Java for example which forces the use of object oriented programming, or C that
forces imperative programming. You can write JavaScript using an object-oriented
paradigm, using prototypes and the new (as of ES6) classes syntax. You can write
JavaScript in functional programming style, with its first class functions, or even in an
imperative style (C-like).
In case you're wondering, JavaScript has nothing to do with Java, it's a poor name choice but
we have to live with it.
JavaScript versions
Let me introduce the term ECMAScript here. We have a complete guide dedicated to
ECMAScript where you can dive into it more, but to start with, you just need to know that
ECMAScript (also called ES) is the name of the JavaScript standard.
JavaScript is an implementation of that standard. That's why you'll hear about ES6, ES2015,
ES2016, ES2017, ES2018 and so on.
For a very long time, the version of JavaScript that all browser ran was ECMAScript 3. Version
4 was cancelled due to feature creep (they were trying to add too many things at once), while
ES5 was a huge version for JS.
6
Introduction to JavaScript
Since then, the ones in charge decided to release one version per year, to avoid having too
much time idle between releases, and have a faster feedback loop.
7
ECMAScript
ECMAScript
ECMAScript is the standard upon which JavaScript is based, and it's often
abbreviated to ES. Discover everything about ECMAScript, and the last
features added in ES6, 7, 8
Introduction
Current ECMAScript version
When is the next version coming out?
What is TC39
ES Versions
ES Next
ES2015 aka ES6
Arrow Functions
A new this scope
Promises
Generators
8
ECMAScript
Classes
Constructor
Super
Getters and setters
Modules
Importing modules
Exporting modules
Template Literals
Default parameters
The spread operator
Destructuring assignments
Enhanced Object Literals
Simpler syntax to include variables
Prototype
super()
Dynamic properties
For-of loop
Map and Set
ES2016 aka ES7
Array.prototype.includes()
Exponentiation Operator
ES2017 aka ES8
String padding
Object.values()
Object.entries()
getOwnPropertyDescriptors()
In what way is this useful?
Trailing commas
Async functions
Why they are useful
A quick example
Multiple async functions in series
Shared Memory and Atomics
Introduction
Whenever you read about JavaScript you'll inevitably see one of these terms:
ES3
9
ECMAScript
ES5
ES6
ES7
ES8
ES2015
ES2016
ES2017
ECMAScript 2017
ECMAScript 2016
ECMAScript 2015
ECMAScript is the standard upon which JavaScript is based, and it's often abbreviated to
ES.
ActionScript (the Flash scripting language), which is losing popularity since Flash will be
officially discontinued in 2020
JScript (the Microsoft scripting dialect), since at the time JavaScript was supported only
by Netscape and the browser wars were at their peak, Microsoft had to build its own
version for Internet Explorer
but of course JavaScript is the most popular and widely used implementation of ES.
Why this weird name? Ecma International is a Swiss standards association who is in charge
of defining international standards.
When JavaScript was created, it was presented by Netscape and Sun Microsystems to Ecma
and they gave it the name ECMA-262 alias ECMAScript.
This press release by Netscape and Sun Microsystems (the maker of Java) might help figure
out the name choice, which might include legal and branding issues by Microsoft which was in
the committee, according to Wikipedia.
After IE9, Microsoft stopped stopped branding its ES support in browsers as JScript and
started calling it JavaScript (at least, I could not find references to it any more)
So as of 201x, the only popular language supporting the ECMAScript spec is JavaScript.
10
ECMAScript
What is TC39
TC39 is the committee that evolves JavaScript.
The members of TC39 are companies involved in JavaScript and browser vendors, including
Mozilla, Google, Facebook, Apple, Microsoft, Intel, PayPal, SalesForce and others.
Every standard version proposal must go through various stages, which are explained here.
ES Versions
I found it puzzling why sometimes an ES version is referenced by edition number and
sometimes by year, and I am confused by the year by chance being -1 on the number, which
adds to the general confusion around JS/ES
Before ES2015, ECMAScript specifications were commonly called by their edition. So ES5 is
the official name for the ECMAScript specification update published in 2009.
Why does this happen? During the process that led to ES2015, the name was changed from
ES6 to ES2015, but since this was done late, people still referenced it as ES6, and the
community has not left the edition naming behind - the world is still calling ES releases by
edition number.
11
ECMAScript
ES Next
ES.Next is a name that always indicates the next version of JavaScript.
So at the time of writing, ES8 has been released, and ES.Next is ES9
Published 4 years after the latest standard revision, ECMAScript 5.1, it also marked the
switch from edition number to year number.
So it should not be named as ES6 (although everyone calls it as such) but ES2015 instead.
ES5 was 10 years in the making, from 1999 to 2009, and as such it was also a
fundamental and very important revision of the language, but now much time has passed
that it's not worth discussing how pre-ES5 code worked.
Since this long time passed between ES5.1 and ES6, the release is full of important new
features and major changes in suggested best practices in developing JavaScript programs.
To understand how fundamental ES2015 is, just keep in mind that with this version, the
specification document went from 250 pages to ~600.
Arrow functions
Promises
Generators
let and const
Classes
Modules
Multiline strings
Template literals
Default parameters
The spread operator|
Destructuring assignments
Enhanced object literals
The for..of loop
Map and Set
12
ECMAScript
Arrow Functions
Arrow functions since their introduction changed how most JavaScript code looks (and works).
to
This is not a breaking change, regular function s will continue to work just as before.
With regular function s this always refers to the nearest function, while with arrow functions
this problem is removed, and you won't need to write var that = this ever again.
Promises
Promises (check the full guide to promises) allow us to eliminate the famous "callback hell",
although they introduce a bit more complexity (which has been solved in ES2017 with async ,
a higher level construct).
13
ECMAScript
Promises have been used by JavaScript developers well before ES2015, with many different
libraries implementations (e.g. jQuery, q, deferred.js, vow...), and the standard put a common
ground across differences.
setTimeout(function() {
console.log('I promised to run after 1s')
setTimeout(function() {
console.log('I promised to run after 2s')
}, 1000)
}, 1000)
as
wait().then(() => {
console.log('I promised to run after 1s')
return wait()
})
.then(() => console.log('I promised to run after 2s'))
Generators
Generators are a special kind of function with the ability to pause itself, and resume later,
allowing other code to run in the meantime.
The code decides that it has to wait, so it lets other code "in the queue" to run, and keeps the
right to resume its operations "when the thing it's waiting for" is done.
All this is done with a single, simple keyword: yield . When a generator contains that
keyword, the execution is halted.
A generator can contain many yield keywords, thus halting itself multiple times, and it's
identified by the *function keyword, which is not to be confused with the pointer dereference
operator used in lower level programming languages such as C, C++ or Go.
14
ECMAScript
function *calculator(input) {
var doubleThat = 2 * (yield (input / 2))
var another = yield (doubleThat)
return (input * doubleThat * another)
}
We initialize it with
calc.next()
This first iteration starts the iterator. The code returns this object:
{
done: false
value: 5
}
What happens is: the code runs the function, with input = 10 as it was passed in the
generator constructor. It runs until it reaches the yield , and returns the content of yield :
input / 2 = 5 . So we got a value of 5, and the indication that the iteration is not done (the
calc.next(7)
{
done: false
value: 14
}
7 was placed as the value of doubleThat . Important: you might read like input / 2 was the
argument, but that's just the return value of the first iteration. We now skip that, and use the
new input value, 7 , and multiply it by 2.
We then reach the second yield, and that returns doubleThat , so the returned value is 14 .
15
ECMAScript
calc.next(100)
{
done: true
value: 14000
}
As the iteration is done (no more yield keywords found) and we just return (input * doubleThat
* another) which amounts to 10 * 14 * 100 .
This means that declaring let variables in a for loop, inside an if or in a plain block is not
going to let that variable "escape" the block, while var s are hoisted up to the function
definition.
In JavaScript moving forward, you'll see little to no var declarations any more, just let and
const .
const in particular, maybe surprisingly, is very widely used nowadays with immutability
Classes
Traditionally JavaScript is the only mainstream language with prototype-based inheritance.
Programmers switching to JS from class-based language found it puzzling, but ES2015
introduced classes, which are just syntactic sugar over the inner working, but changed a lot
how we build JavaScript programs.
Now inheritance is very easy and resembles other object-oriented programming languages:
class Person {
constructor(name) {
this.name = name
}
hello() {
return 'Hello, I am ' + this.name + '.'
16
ECMAScript
}
}
Classes do not have explicit class variable declarations, but you must initialize any variable in
the constructor.
Constructor
Classes have a special method called constructor which is called when a class is initialized
via new .
Super
The parent class can be referenced using super() .
class Person {
get fullName() {
return `${this.firstName} ${this.lastName}`
}
}
class Person {
set age(years) {
this.theAge = years
}
}
Modules
17
ECMAScript
Before ES2015, there were at least 3 major modules competing standards, which fragmented
the community:
AMD
RequireJS
CommonJS
Importing modules
Importing is done via the import ... from ... construct:
Exporting modules
You can write modules and export anything to other modules using the export keyword:
Template Literals
Template literals are a new syntax to create strings:
They provide a way to embed expressions into strings, effectively interpolating the values, by
using the ${a_variable} syntax:
18
ECMAScript
string
is awesome!`
Default parameters
Functions now support default parameters:
const a = [1, 2, 3]
const b = [...a, 4, 5, 6]
const c = [...a]
Using strings, the spread operator creates an array with each char in the string:
19
ECMAScript
This operator has some pretty useful applications. The most important one is the ability to use
an array as function argument in a very simple way:
(in the past you could do this using f.apply(null, a) but that's not as nice and readable)
Destructuring assignments
Given an object, you can extract just some values and put them into named variables:
const person = {
firstName: 'Tom',
lastName: 'Cruise',
actor: true,
age: 54, //made up
}
const a = [1,2,3,4,5]
[first, second, , , fifth] = a
20
ECMAScript
you can do
Prototype
A prototype can be specified with
super()
Dynamic properties
const x = {
['a' + '_' + 'b']: 'z'
}
x.a_b //z
For-of loop
ES5 back in 2009 introduced forEach() loops. While nice, they offered no way to break, like
for loops always did.
ES2015 introduced the for-of loop, which combines the conciseness of forEach with the
ability to break:
21
ECMAScript
Compared to ES6, ES7 is a tiny release for JavaScript, containing just two features:
Array.prototype.includes
Exponentiation Operator
Array.prototype.includes()
This feature introduces a more readable syntax for checking if an array contains an element.
With ES6 and lower, to check if an array contained an element you had to use indexOf , which
checks the index in the array, and returns -1 if the element is not there.
if (![1,2].indexOf(3)) {
console.log('Not found')
}
if (![1,2].includes(3)) {
console.log('Not found')
}
Exponentiation Operator
The exponentiation operator ** is the equivalent of Math.pow() , but brought into the
language instead of being a library function.
22
ECMAScript
Math.pow(4, 2) == 4 ** 2
The ** operator is standardized across many languages including Python, Ruby, MATLAB,
Lua, Perl and many others.
Compared to ES6, ES8 is a tiny release for JavaScript, but still it introduces very useful
features:
String padding
Object.values
Object.entries
Object.getOwnPropertyDescriptors()
Trailing commas in function parameter lists and calls
Async functions
Shared memory and atomics
String padding
The purpose of string padding is to add characters to a string, so it reaches a specific
length.
padStart(targetLength [, padString])
padEnd(targetLength [, padString])
Sample usage:
padStart()
'test'.padStart(4) 'test'
'test'.padStart(5) ' test'
'test'.padStart(8) ' test'
'test'.padStart(8, 'abcd') 'abcdtest'
23
ECMAScript
padEnd()
'test'.padEnd(4) 'test'
Object.values()
This method returns an array containing all the object own property values.
Usage:
Object.entries()
This method returns an array containing all the object own properties, as an array of [key,
value] pairs.
Usage:
getOwnPropertyDescriptors()
This method returns all own (non-inherited) properties descriptors of an object.
Any object in JavaScript has a set of properties, and each of these properties has a descriptor.
A descriptor is a set of attributes of a property, and it's composed by a subset of the following:
24
ECMAScript
Object.getOwnPropertyDescriptors(obj) accepts and object, and returns an object with the set
of descriptors.
However there is a problem with that, because it does not correctly copies properties with non-
default attributes.
If an object for example has just a setter, it's not correctly copied to a new object, using
Object.assign() .
const person1 = {
set name(newName) {
console.log(newName)
}
}
const person2 = {}
Object.assign(person2, person1)
const person3 = {}
Object.defineProperties(person3,
Object.getOwnPropertyDescriptors(person1))
person1.name = 'x'
"x"
25
ECMAScript
person2.name = 'x'
person3.name = 'x'
"x"
The same limitation goes for shallow cloning objects with Object.create().
Trailing commas
This feature allows to have trailing commas in function declarations, and in functions calls:
doSomething('test2', 'test2',)
This change will encourage developers to stop the ugly "comma at the start of the line" habit.
Async functions
Check the dedicated post about async/await
ES2017 introduced the concept of async functions, and it's the most important change
introduced in this ECMAScript edition.
Async functions are a combination of promises and generators to reduce the boilerplate
around promises, and the "don't break the chain" limitation of chaining promises.
When Promises were introduced in ES2015, they were meant to solve a problem with
asynchronous code, and they did, but over the 2 years that separated ES2015 and ES2017, it
was clear that promises could not be the final solution. Promises were introduced to solve the
famous callback hell problem, but they introduced complexity on their own, and syntax
complexity. They were good primitives around which a better syntax could be exposed to the
developers: enter async functions.
A quick example
26
ECMAScript
function doSomethingAsync() {
return new Promise((resolve) => {
setTimeout(() => resolve('I did something'), 3000)
})
}
console.log('Before')
doSomething()
console.log('After')
The above code will print the following to the browser console:
Before
After
I did something //after 3s
function promiseToDoSomething() {
return new Promise((resolve)=>{
setTimeout(() => resolve('I did something'), 10000)
})
}
watchOverSomeoneWatchingSomeoneDoingSomething().then((res) => {
console.log(res)
})
27
ECMAScript
They offer a messaging protocol via events. Since ES2017, you can create a shared memory
array between web workers and their creator, using a SharedArrayBuffer .
Since it's unknown how much time writing to a shared memory portion takes to propagate,
Atomics are a way to enforce that when reading a value, any kind of writing operation is
completed.
Any more detail on this can be found in the spec proposal, which has since been implemented.
28
Lexical Structure
Lexical Structure
A deep dive into the building blocks of JavaScript: unicode, semicolons,
white space, case sensitivity, comments, literals, identifiers and reserved
words
Unicode
Semicolons
White space
Case sensitive
Comments
Literals and Identifiers
Reserved words
Unicode
JavaScript is written in Unicode. This means you can use Emojis as variable names, but more
importantly, you can write identifiers in any language, for example Japanese or Chinese, with
some rules.
Semicolons
JavaScript has a very C-like syntax, and you might see lots of code samples that feature
semicolons at the end of each line.
Semicolons aren't mandatory, and JavaScript does not have any problem in code that does
not use them, and lately many developers, especially those coming from languages that do not
have semicolons, started avoiding using them.
You just need to avoid doing strange things like typing statements on multiple lines
return
variable
or starting a line with parentheses ( [ or ( ) and you'll be safe 99.9% of the times (and your
linter will warn you).
It goes to personal preference, and lately I have decided to never add useless semicolons,
so on this site you'll never see them.
29
Lexical Structure
White space
JavaScript does not consider white space meaningful. Spaces and line breaks can be added
in any fashion you might like, even though this is in theory.
In practice, you will most likely keep a well defined style and adhere to what people commonly
use, and enforce this using a linter or a style tool such as Prettier.
Case sensitive
JavaScript is case sensitive. A variable named something is different from Something .
Comments
You can use two kind of comments in JavaScript:
/* */
//
The first can span over multiple lines and needs to be closed.
The second comments everything that's on its right, on the current line.
5
'Test'
true
['a', 'b']
{color: 'red', shape: 'Rectangle'}
30
Lexical Structure
Test
test
TEST
_test
Test1
$test
Reserved words
You can't use as identifiers any of the following words:
break
do
instanceof
typeof
case
else
new
var
catch
finally
return
void
continue
for
switch
while
debugger
function
this
with
default
if
throw
delete
in
try
class
enum
extends
super
const
export
import
implements
let
private
public
interface
package
31
Lexical Structure
protected
static
yield
32
Variables
Variables
A variable is a literal assigned to an identifier, so you can reference and use
it later in the program. Learn how to declare one with JavaScript
Variables in JavaScript do not have any type attached. Once you assign a specific literal type
to a variable, you can later reassign the variable to host any other type, without type errors or
any issue.
A variable must be declared before you can use it. There are 3 ways to do it, using var , let
or const , and those 3 ways differ in how you can interact with the variable later on.
Using var
Until ES2015, var was the only construct available for defining variables.
var a = 0
If you forget to add var you will be assigning a value to an undeclared variable, and the
results might vary.
In modern environments, with strict mode enabled, you will get an error. In older environments
(or with strict mode disabled) this will simply initialize the variable and assign it to the global
object.
If you don't initialize the variable when you declare it, it will have the undefined value until you
assign a value to it.
33
Variables
var a = 1
var a = 2
You can also declare multiple variables at once in the same statement:
var a = 1, b = 2
A variable initialized with var outside of any function is assigned to the global object, has a
global scope and is visible everywhere. A variable initialized with var inside a function is
assigned to that function, it's local and is visible only inside it, just like a function parameter.
Any variable defined into a function with the same name of a global variable takes precedence
over the global variable, shadowing it.
It's important to understand that a block (identified by a pair of curly braces) does not define a
new scope. A new scope is only created when a function is created, because var has not
block scope, but function scope.
Inside a function, any variable defined in it is visible throughout all the function code, even if
the variable is declared at the end of the function it can still be referenced in the beginning,
because JavaScript before executing the code actually moves all variables on top (something
that is called hoisting). To avoid confusion, always declare variables at the beginning of a
function.
Using let
let is a new feature introduced in ES2015 and it's essentially a block scoped version of
var . Its scope is limited to the block, statement or expression where it's defined, and all the
Modern JavaScript developers might choose to only use let and completely discard the use
of var .
If let seems an obscure term, just read let color = 'red' as let the color be red and
all has much more sense
Defining let outside of any function - contrary to var - does not create a global variable.
34
Variables
Using const
Variables declared with var or let can be changed later on in the program, and
reassigned. A once a const is initialized, its value can never be changed again, and it can't
be reassigned to a different value.
const a = 'test'
We can't assign a different literal to the a const. We can however mutate a if it's an object
that provides methods that mutate its contents.
const does not provide immutability, just makes sure that the reference can't be changed.
Modern JavaScript developers might choose to always use const for variables that don't
need to be reassigned later in the program.
Why? Because we should always use the simplest construct available to avoid making
errors down the road.
35
Types
Types
You might sometimes read that JS is untyped, but that's incorrect. It's true
that you can assign all sorts of different types to a variable, but JavaScript
has types. In particular, it provides primitive types, and object types.
Primitive types
Numbers
Strings
Template strings
Booleans
null
undefined
Object types
Primitive types
Primitive types are
Numbers
Strings
Booleans
null
undefined
Numbers
Internally, JavaScript has just one type for numbers: every number is a float.
A numeric literal is a number represented in the source code, amd depending on how it's
written, it can be an integer literal or a floating point literal.
Integers:
10
5354576767321
0xCC //hex
36
Types
Floats:
3.14
.1234
5.2e4 //5.2 * 10^4
Strings
A string type is a sequence of characters. It's defined in the source code as a string literal,
which is enclosed in quotes or double quotes
'A string'
"Another string"
"A \
string"
A string can contain escape sequences that can be interpreted when the string is printed, like
\n to create a new line. The backslash is also useful when you need to enter for example a
quote in a string enclosed in quotes, to prevent the char to be interpreted as a closing quote:
'I\'m a developer'
Template strings
Introduced in ES2015, template strings are string literals that allow a more powerful way to
define strings.
`a string`
You can perform string substitution, embedding the result of any JS expression:
37
Types
`a string
with
${something}`
Booleans
JavaScript defines two reserved words for booleans: true and false. Many comparision
operations == === < > (and so on) return either one or the other.
if , while statements and other control structures use booleans to determine the flow of the
program.
They don't just accept true or false, but also accept truthy and falsy values.
0
-0
NaN
undefined
null
'' //empty string
null
null is a special value that indicates the absence of a value.
It's a common concept in other languages as well, can be known as nil or None in Python
for example.
undefined
undefined indicates that a variable has not been initialized and the value is absent.
It's commonly returned by functions with no return value. When a function accepts a
parameter but that's not set by the caller, it's undefined.
38
Types
Object types
Anything that's not a primitive type is an object type.
Functions, arrays and what we call objects are object types. They are special on their own, but
they inherit many properties of objects, like having properties and also having methods that
can act on those properties.
39
Expressions
Expressions
Expressions are units of code that can be evaluated and resolve to a value.
Expressions in JS can be divided in categories.
Arithmetic expressions
String expressions
Primary expressions
Array and object initializers expressions
Logical expressions
Left-hand-side expressions
Property access expressions
Object creation expressions
Function definition expressions
Invocation expressions
Arithmetic expressions
Under this category go all expressions that evaluate to a number:
1 / 2
i++
i -= 2
i * 2
String expressions
Expressions that evaluate to a string:
Primary expressions
Under this category go variable references, literals and constants:
2
0.02
'something'
40
Expressions
true
false
this //the current object
undefined
i //where i is a variable or a constant
function
class
function* //the generator function
yield //the generator pauser/resumer
yield* //delegate to another generator or iterator
async function* //async function expression
await //async function pause/resume/wait for completion
/pattern/i //regex
() // grouping
Logical expressions
Logical expressions make use of logical operators and resolve to a boolean value:
a && b
a || b
!a
Left-hand-side expressions
new //create an instance of a constructor
super //calls the parent constructor
...obj //expression using the spread operator
41
Expressions
Invocation expressions
The syntax for calling a function or method
a.x(2)
window.resize()
42
Prototypal inheritance
Prototypal inheritance
JavaScript is quite unique in the popular programming languages landscape
because of its usage of prototypal inheritance. Let's find out what that means
JavaScript is quite unique in the popular programming languages landscape because of its
usage of prototypal inheritance.
Every single JavaScript object has a property, called prototype , which points to a different
object.
Our object uses that object prototype to inherit properties and methods.
Say you have an object created using the object literal syntax:
const car = {}
const list = []
//or
const list = new Array()
43
Prototypal inheritance
I use the __proto__ property here, which is non-standard but widely implemented in
browsers. A more reliable way to get a prototype is to use Object.getPrototypeOf(new
Object())
All the properties and methods of the prototype are available to the object that has that
prototype:
Array.prototype.__proto__ == Object.prototype
If you wonder what's the prototype of the Object.prototype, there is no prototype. It's a special
snowflake ❄ .
The above example you saw is an example of the prototype chain at work.
I can make an object that extends Array and any object I instantiate using it, will have Array
and Object in its prototype chain and inherit properties and methods from all the ancestors.
In addition to using the new operator to create an object, or using the literals syntax for
objects and arrays, you can instantiate an object using Object.create() .
44
Prototypal inheritance
You can check the prototype of an object using the isPrototypeOf() method:
Array.isPrototypeOf(list) //true
45
Classes
Classes
In 2015 the ECMAScript 6 (ES6) standard introduced classes. Learn all about
them
Before that, JavaScript only had a quite unique way to implement inheritance. Its prototypal
inheritance, while in my opinion great, was different from any other popular programming
language.
People coming from Java or Python or other languages had a hard time understanding the
intricacies of prototypal inheritance, so the ECMAScript committee decided to introduce a
syntactic sugar on top of them, and resemble how classes-based inheritance works in other
popular implementations.
This is important: JavaScript under the hoods is still the same, and you can access an object
prototype in the usual way.
A class definition
This is how a class looks.
class Person {
constructor(name) {
this.name = name
}
hello() {
return 'Hello, I am ' + this.name + '.'
}
}
A class has an identifier, which we can use to create new objects using new
ClassIdentifier() .
When the object is initialized, the constructor method is called, with any parameters passed.
A class also has as many methods as it needs. In this case hello is a method and can be
called on all objects derived from this class:
46
Classes
Classes inheritance
A class can extend another class, and objects initialized using that class inherit all the
methods of both classes.
If the inherited class has a method with the same name as one of the classes higher in the
hierarchy, the closest method takes precedence:
Classes do not have explicit class variable declarations, but you must initialize any variable in
the constructor.
Inside a class, you can reference the parent class calling super() .
Static methods
Normally methods are defined on the instance, not on the class.
class Person {
static genericHello() {
return 'Hello'
}
}
Person.genericHello() //Hello
Private methods
JavaScript does not have a built-in way to define private or protected methods.
47
Classes
class Person {
constructor(name) {
this.name = name
}
set name(value) {
this.name = value
}
get name() {
return this.name
}
}
If you only have a getter, the property cannot be set, and any attempt at doing so will be
ignored:
class Person {
constructor(name) {
this.name = name
}
get name() {
return this.name
}
}
If you only have a setter, you can change the value but not access it from the outside:
class Person {
constructor(name) {
this.name = name
}
set name(value) {
this.name = value
}
}
48
Classes
49
Exceptions
Exceptions
When the code runs into an unexpected problem, the JavaScript idiomatic
way to handle this situation is through exceptions
When the code runs into an unexpected problem, the JavaScript idiomatic way to handle this
situation is through exceptions.
Creating exceptions
An exception is created using the throw keyword:
throw value
where value can be any JavaScript value including a string, a number or an object.
As soon as JavaScript executes this line, the normal program flow is halted and the control is
held back to the nearest exception handler.
Handling exceptions
An exception handler is a try / catch statement.
Any exception raised in the lines of code included in the try block is handled in the
corresponding catch block:
try {
//lines of code
} catch (e) {
finally
50
Exceptions
To complete this statement JavaScript has another statement called finally , which contains
code that is executed regardless of the program flow, if the exception was handled or not, if
there was an exception or if there wasn't:
try {
//lines of code
} catch (e) {
} finally {
You can use finally without a catch block, to serve as a way to clean up any resource you
might have opened in the try block, like files or network requests:
try {
//lines of code
} finally {
try {
//lines of code
try {
//other lines of code
} finally {
//other lines of code
}
} catch (e) {
If an exception is raised in the inner try , it's handled in the outer catch block.
51
Semicolons
Semicolons
JavaScript semicolons are optional. I personally like avoiding using
semicolons in my code, but many people prefer them.
Semicolons in JavaScript divide the community. Some prefer to use them always, no matter
what. Others like to avoid them.
After using semicolons for years, in the fall of 2017 I decided to try avoiding them as needed,
and I did set up Prettier to automatically remove semicolons from my code, unless there is a
particular code construct that requires them.
Now I find it natural to avoid semicolons, I think the code looks better and it's cleaner to read.
This is all possible because JavaScript does not strictly require semicolons. When there is a
place where a semicolon was needed, it adds it behind the scenes.
It's important to know the rules that power semicolons, to avoid writing code that will generate
bugs because does not behave like you expect.
1. when the next line starts with code that breaks the current one (code can spawn on
multiple lines)
2. when the next line starts with a } , closing the current block
3. when the end of the source code file is reached
4. when there is a return statement on its own line
5. when there is a break statement on its own line
6. when there is a throw statement on its own line
7. when there is a continue statement on its own line
52
Semicolons
Take this:
You'll get the error Uncaught TypeError: Cannot read property 'forEach' of undefined because
based on rule 1 JavaScript tries to interpret the code as
(1 + 2).toString()
prints "3" .
const a = 1
const b = 2
const c = a + b
(a + b).toString()
const a = 1
const b = 2
const c = a + b(a + b).toString()
(() => {
return
{
color: 'white'
}
})()
53
Semicolons
You'd expect the return value of this immediately-invoked function to be an object that
contains the color property, but it's not. Instead, it's undefined , because JavaScript inserts a
semicolon after return .
Instead you should put the opening bracket right after return :
(() => {
return {
color: 'white'
}
})()
1 + 1
-1 + 1 === 0 ? alert(0) : alert(2)
Conclusion
Be careful. Some people are very opinionated on semicolons. I don't care honestly, the tool
gives us the option not to use it, so we can avoid semicolons.
I'm not suggesting anything, other than picking your own decision.
We just need to pay a bit of attention, even if most of the times those basic scenarios never
show up in your code.
be careful with return statements. If you return something, add it on the same line as the
return (same for break , throw , continue )
never start a line with parentheses, those might be concatenated with the previous line to
form a function call, or array element reference
And ultimately, always test your code to make sure it does what you want
54
Semicolons
55
Quotes
Quotes
An overview of the quotes allowed in JavaScript and their unique features
single quotes
double quotes
backticks
There's little to no difference in using one or the other. The only difference lies in having to
escape the quote character you use to delimit the string:
There are various style guides that recommend always using one style vs the other.
I personally prefer single quotes all the time, and use double quotes only in HTML.
Backticks are a recent addition to JavaScript, since they were introduced with ES6 in 2015.
Multiline strings are also possible using regular strings, using escape characters:
Not just that. You can interpolate variables using the ${} syntax:
56
Quotes
on ${1+1} lines`
I cover backticks-powered strings (called template literals) in a separate article, that dives
more into the nitty-gritty details.
57
Functions
Functions
Learn all about functions, from the general overview to the tiny details that
will improve how you use them
Introduction
Syntax
Parameters
Return values
Nested functions
Object Methods
this in Arrow Functions
Introduction
Everything in JavaScript happens in functions.
A function is a block of code, self contained, that can be defined once and run any times you
want.
58
Functions
Functions in JavaScript are objects, a special kind of objects: function objects. Their
superpower lies in the fact that they can be invoked.
In addition, functions are said to be first class functions because they can be assigned to a
value, and they can be passed as arguments and used as a return value.
Syntax
Let's start with the "old", pre-ES6/ES2015 syntax. Here's a function declaration:
function dosomething(foo) {
// do something
}
Named function expressions are similar, but play nicer with the stack call trace, which is
useful when an error occurs - it holds the name of the function:
ES6/ES2015 introduced arrow functions, which are especially nice to use when working with
inline functions, as parameters or callbacks:
Arrow functions have an important difference from the other function definitions above, we'll
see which one later as it's an advanced topic.
Parameters
A function can have one or more parameters.
59
Functions
Starting with ES6/ES2015, functions can have default values for the parameters:
This allows you to call a function without filling all the parameters:
dosomething(3)
dosomething()
ES2018 introduced trailing commas for parameters, a feature that helps reducing bugs due to
missing commas when moving around parameters (e.g. moving the last in the middle):
dosomething(2, 'ho!')
You can wrap all your arguments in an array, and use the spread operator when calling the
function:
With many parameters, remembering the order can be difficult. Using objects, destructuring
allows to keep the parameter names:
60
Functions
console.log(foo) // 2
console.log(bar) // 'ho!'
}
const args = { foo: 2, bar: 'ho!' }
dosomething(args)
Return values
Every function returns a value, which by default is undefined .
Any function is terminated when its lines of code end, or when the execution flow finds a
return keyword.
When JavaScript encounters this keyword it exits the function execution and gives control
back to its caller.
If you pass a value, that value is returned as the result of the function:
To simulate returning multiple values, you can return an object literal, or an array, and use a
destructuring assignment when calling the function.
Using arrays:
61
Functions
Using objects:
Nested functions
Functions can be defined inside other functions:
62
Functions
The nested function is scoped to the outside function, and cannot be called from the outside.
Object Methods
When used as object properties, functions are called methods:
const car = {
brand: 'Ford',
model: 'Fiesta',
start: function() {
console.log(`Started`)
}
}
car.start()
There's an important behavior of Arrow Functions vs regular Functions when used as object
methods. Consider this example:
const car = {
brand: 'Ford',
model: 'Fiesta',
start: function() {
console.log(`Started ${this.brand} ${this.model}`)
},
stop: () => {
console.log(`Stopped ${this.brand} ${this.model}`)
}
}
63
Functions
This is because the handling of this is different in the two functions declarations style. this
in the arrow function refers to the enclosing function context, which in this case is the window
object:
64
Functions
This implies that arrow functions are not suitable to be used for object methods and
constructors (arrow function constructors will actually raise a TypeError when called).
;(function dosomething() {
console.log('executed')
})()
They are very handy, as you don't need to separately call the function after its definition.
Function Hoisting
JavaScript before executing your code reorders it according to some rules.
Functions in particular are moved at the top of their scope. This is why it's legal to write
dosomething()
function dosomething() {
console.log('did something')
}
Internally, JavaScript moves the function before its call, along with all the other functions found
in the same scope:
65
Functions
function dosomething() {
console.log('did something')
}
dosomething()
Now, if you use named function expressions, since you're using variables something different
happens. The variable declaration is hoisted, but not the value, so not the function.
dosomething()
const dosomething = function dosomething() {
console.log('did something')
}
const dosomething
dosomething()
dosomething = function dosomething() {
console.log('did something')
}
The same happens for let declarations. var declarations do not work either, but with a
different error:
66
Functions
This is because var declarations are hoisted and initialized with undefined as a value, while
const and let are hoisted but not initialized.
67
Arrow Functions
Arrow Functions
Arrow Functions are one of the most impactful changes in ES6/ES2015, and
they are widely used nowadays. They slightly differ from regular functions.
Find out how
Arrow functions were introduced in ES6 / ECMAScript 2015, and since their introduction they
changed forever how JavaScript code looks (and works).
In my opinion this change was so welcoming that you now rarely see in modern codebases the
usage of the function keyword.
Visually, it’s a simple and welcome change, which allows you to write functions with a shorter
syntax, from:
to
If the function body contains just a single statement, you can omit the parentheses and write
all on a single line:
If you have one (and just one) parameter, you could omit the parentheses completely:
Thanks to this short syntax, arrow functions encourage the use of small functions.
Implicit return
68
Arrow Functions
Arrow functions allow you to have an implicit return: values are returned without having to use
the return keyword.
myFunction() //'test'
Another example, returning an object (remember to wrap the curly brackets in parentheses to
avoid it being considered the wrapping function body brackets):
context and also varies depending on the mode of JavaScript (strict mode or not).
It's important to clarify this concept because arrow functions behave very differently compared
to regular functions.
When defined as a method of an object, in a regular function this refers to the object, so you
can do:
const car = {
model: 'Fiesta',
manufacturer: 'Ford',
fullName: function() {
return `${this.manufacturer} ${this.model}`
}
}
The this scope with arrow functions is inherited from the execution context. An arrow
function does not bind this at all, so its value will be looked up in the call stack, so in this
code car.fullName() will not work, and will return the string "undefined undefined" :
const car = {
model: 'Fiesta',
manufacturer: 'Ford',
fullName: () => {
69
Arrow Functions
Arrow functions cannot be used as constructors as well, when instantiating an object will raise
a TypeError .
This is where regular functions should be used instead, when dynamic context is not
needed.
This is also a problem when handling events. DOM Event listeners set this to be the target
element, and if you rely on this in an event handler, a regular function is necessary:
70
Closures
Closures
A gentle introduction to the topic of closures, key to understanding how
JavaScript functions work
If you've ever written a function in JavaScript, you already made use of closures.
It's a key topic to understand, which has implications on the things you can do.
When a function is run, it's executed with the scope that was in place when it was defined,
and not with the state that's in place when it is executed.
A function remembers its Lexical Scope, and it's able to access variables that were defined in
the parent scope.
bark(`Roger`)
71
Closures
bark()
Let's make one last example, which reuses prepareBark for two different dogs:
rogerBark()
sydBark()
This prints
Roger barked!
Syd barked!
As you can see, the state of the variable say is linked to the function that's returned from
prepareBark() .
Also notice that we redefine a new say variable the second time we call prepareBark() , but
that does not affect the state of the first prepareBark() scope.
This is how a closure works: the function that's returned keeps the original state in its scope.
72
Arrays
Arrays
JavaScript arrays over time got more and more features, sometimes it's
tricky to know when to use some construct vs another. This post aims to
explain what you should use, as of 2018
Initialize array
Get length of the array
Iterating the array
Every
Some
Iterate the array and return a new one with the returned result of a function
Filter an array
Reduce
forEach
for..of
for
@@iterator
Adding to an array
Add a the end
73
Arrays
JavaScript arrays over time got more and more features, sometimes it's tricky to know when to
use some construct vs another. This post aims to explain what you should use in 2018.
Initialize array
const a = []
const a = [1, 2, 3]
const a = Array.of(1, 2, 3)
const a = Array(6).fill(1) //init an array of 6 items of value 1
Don't use the old syntax (just use it for typed arrays)
74
Arrays
Every
a.every(f)
Some
a.some(f)
Iterate the array and return a new one with the returned result
of a function
const b = a.map(f)
Iterates a and builds a new array with the result of executing f() on each a element
Filter an array
const b = a.filter(f)
Iterates a and builds a new array with elements of a that returned true when executing
f() on each a element
Reduce
reduce() executes a callback function on all the items of the array and allows to progressively
compute a result. If initialValue is specified, accumulator in the first iteration will equal to
that value.
Example:
75
Arrays
}, 1)
// return value is 24
forEach
ES6
a.forEach(f)
Example:
a.forEach(v => {
console.log(v)
})
for..of
ES6
for (let v of a) {
console.log(v)
}
for
Iterates a , can be stopped using return or break and an iteration can be skipped using
continue
@@iterator
ES6
76
Arrays
const a = [1, 2, 3]
let it = a[Symbol.iterator]()
console.log(it.next().value) //1
console.log(it.next().value) //2
console.log(it.next().value) //3
let it = a.entries()
console.log(it.next().value) //[0, 1]
console.log(it.next().value) //[1, 2]
console.log(it.next().value) //[2, 3]
let it = a.keys()
console.log(it.next().value) //0
console.log(it.next().value) //1
console.log(it.next().value) //2
.next() returns undefined when the array ends. You can also detect if the iteration ended by
looking at it.next() which returns a value, done pair. done is always false until the last
element, which returns true .
Adding to an array
Add a the end
a.push(4)
a.unshift(0)
a.unshift(-2, -1)
77
Arrays
a.pop()
a.shift()
At a random position
a.indexOf()
Returns the index of the first matching item found, or -1 if not found
a.lastIndexOf()
78
Arrays
Returns the index of the last matching item found, or -1 if not found
ES6
Returns the first item that returns true. Returns undefined if not found.
The above line will return the first element in the array that has id === my_id .
findIndex returns the index of the first item that returns true, and if not found, it returns
undefined :
ES7
a.includes(value)
a.includes(value, i)
79
Arrays
a.reverse()
a.join()
Returns a string concatenation of the array elements. Pass a parameter to add a custom
separator:
a.join(', ')
80
Arrays
81
Loops
Loops
JavaScript provides many way to iterate through loops. This tutorial explains
all the various loop possibilities in modern JavaScript
Introduction
for
forEach
do...while
while
for...in
for...of
for...in vs for...of
Introduction
JavaScript provides many way to iterate through loops. This tutorial explains each one with a
small example and the main properties.
for
82
Loops
forEach
Introduced in ES5. Given an array, you can iterate over its properties using list.forEach() :
//index is optional
list.forEach(item => console.log(item))
do...while
do {
if (something) break
} while (true)
do {
if (something) continue
83
Loops
while
while (true) {
if (something) break
}
while (true) {
if (something) continue
The difference with do...while is that do...while always execute its cycle at least once.
for...in
Iterates all the enumerable properties of an object, giving the property names.
for...of
ES2015 introduced the for...of loop, which combines the conciseness of forEach with the
ability to break:
84
Loops
Notice the use of const . This loop creates a new scope in every iteration, so we can safely
use that instead of let .
for...in vs for...of
85
Events
Events
JavaScript in the browser uses an event-driven programming model.
Everything starts by following an event. This is an introduction to JavaScript
events and how event handling works
Introduction
Event handlers
Inline event handlers
DOM on-event handlers
Using addEventListener()
Listening on different elements
The Event object
Event bubbling and event capturing
Stopping the propagation
Popular events
Load
Mouse events
Keyboard events
Scroll
Debouncing
86
Events
Introduction
JavaScript in the browser uses an event-driven programming model.
The event could be the DOM is loaded, or an asynchronous request that finishes fetching, or a
user clicking an element or scrolling the page, or the user types on the keyboard.
Event handlers
You can respond to any event using an Event Handler, which is just a function that's called
when an event occurs.
You can register multiple handlers for the same event, and they will all be called when that
event happens.
window.onload = () => {
//window loaded
}
87
Events
Using addEventListener()
This is the modern way. This method allows to register as many handlers as we need, and it's
the most popular you will find:
window.addEventListener('load', () => {
//window loaded
})
This method allows to register as many handlers as we need, and it's the most popular you will
find.
Note that IE8 and below did not support this, and instead used its own attachEvent()
API. Keep it in mind if you need to support older browsers.
88
Events
Other properties are provided by specific kind of events, as Event is an interface for different
specific events:
MouseEvent
KeyboardEvent
DragEvent
FetchEvent
... and others
Each of those has a MDN page linked, so you can inspect all their properties.
For example when a KeyboardEvent happens, you can check which key was pressed, in ar
readable format ( Escape , Enter and so on) by checking the key property:
<div id="container">
<button>Click me</button>
</div>
You want to track when users click on the button, and you have 2 event listeners, one on
button , and one on #container . Remember, a click on a child element will always propagate
Those event listeners will be called in order, and this order is determined by the event
bubbling/capturing model used.
89
Events
Bubbling means that the event propagates from the item that was clicked (the child) up to all
its parent tree, starting from the nearest one.
In our example, the handler on button will fire before the #container handler.
Capturing is the opposite: the outer event handlers are fired before the more specific handler,
the one on button .
You can choose to adopt event capturing by applying a third argument to addEventListener,
setting it to true :
document.getElementById('container').addEventListener(
'click',
() => {
//window loaded
},
true
)
The order follows this principle: the DOM goes through all elements starting from the Window
object, and goes to find the item that was clicked. While doing so, it calls any event handler
associated to the event (capturing phase).
Once it reaches the target, it then repeats the journey up to the parents tree until the Window
object, calling again the event handlers (bubbling phase).
<html>
<body>
<section>
<a id="my-link" ...>
You can stop the propagation by calling the stopPropagation() method of an Event, usually at
the end of the event handler:
90
Events
event.stopPropagation()
})
Popular events
Here's a list of the most common events you will likely handle.
Load
load is fired on window and the body element when the page has finished loading.
Mouse events
click fires when a mouse button is clicked. dblclick when the mouse is clicked two times.
Of course in this case click is fired just before this event. mousedown , mousemove and
mouseup can be used in combination to track drag-and-drop events. Be careful with
mousemove , as it fires many times during the mouse movement (see debouncing later)
Keyboard events
keydown fires when a keyboard button is pressed (and any time the key repeats while the
Scroll
The scroll event is fired on window every time you scroll the page. Inside the event handler
you can check the current scrolling position by checking window.scrollY .
Keep in mind that this event is not a one-time thing. It fires a lot of times during scrolling, not
just at the end or beginning of the scrolling, so don't do any heavy computation or
manipulation in the handler - use debouncing instead.
Debouncing
91
Events
As we mentioned above, mousemove and scroll are two events that are not fired one-time
per event, but rather they continuously call their event handler function during all the duration
of the action.
This is because they provide coordinates so you can track what's happening.
If you perform a complex operation in the event handler, you will affect the performance and
cause a sluggish experience to your site users.
Libraries that provide debouncing like Lodash implement it in 100+ lines of code, to handle
every possible use case. A simple and easy to understand implementation is this, which uses
setTimeout to cache the scroll event every 100ms:
92
The Event Loop
Introduction
Blocking the event loop
The call stack
A simple event loop explanation
Queuing function execution
The Message Queue
ES6 Job Queue
Introduction
The Event Loop is one of the most important aspects to understand about JavaScript.
I've programmed for years with JavaScript, yet I've never fully understood how things
work under the hoods. It's completely fine to not know this concept in details, but as usual
it's helpful to know how it works, and also you might just be a little curious at this point.
This post aims to explain the inner details of how JavaScript works with a single thread, and
how it handles asynchronous functions.
Your JavaScript code runs single threaded. There is just one thing happening at a time.
This is a limitation that's actually very helpful, as it simplifies a lot how you program without
worrying about concurrency issues.
You just need to pay attention to how you write your code, and avoid anything that could block
the thread, like synchronous network calls or infinite loops.
In general, in most browsers there is an event loop for every browser tab, to make every
process isolated and avoid a web page with infinite loops or heavy processing to block your
entire browser.
The environment manages multiple concurrent event loops, to handle API calls for example.
Web Workers run in their own event loop as well.
You mainly need to be concerned that your code will run on a single event loop, and write
code with this thing in mind to avoid blocking it.
93
The Event Loop
Almost all the I/O primitives in JavaScript are non-blocking. Network requests, Node.js
filesystem operations, and so on. Being blocking is the exception, and this is why JavaScript is
based so much on callbacks, and more recently on promises and async/await.
The event loop continuously checks the call stack to see if there's any function that needs to
run.
While doing so, it adds any function call it finds to the call stack, and executes each one in
order.
You know the error stack trace you might be familiar with, in the debugger or in the browser
console? The browser looks up the function names in the call stack to inform you which
function originates the current call:
94
The Event Loop
foo()
95
The Event Loop
foo
bar
baz
as expected.
When this code runs, first foo() is called. Inside foo() we first call bar() , then we call
baz() .
The event loop on every iteration looks if there's something in the call stack, and executes it:
96
The Event Loop
97
The Event Loop
The use case of setTimeout(() => {}), 0) is to call a function, but execute it once every other
function in the code has executed.
foo()
foo
baz
bar
When this code runs, first foo() is called. Inside foo() we first call setTimeout, passing bar as
an argument, and we instruct it to run immediately as fast as it can, passing 0 as the timer.
Then we call baz().
98
The Event Loop
Here is the execution order for all the functions in our program:
99
The Event Loop
100
The Event Loop
The Message Queue is also where user-initiated events like click or keyboard events, or fetch
responses are queued before your code has the opportunity to react to them. Or also DOM
events like onLoad .
The loop gives priority to the call stack, and it first processes everything it finds in the
call stack, and once there's nothing in there, it goes to pick up things in the event
queue.
We don't have to wait for functions like setTimeout , fetch or other things to do their own work,
because they are provided by the browser, and they live on their own threads. For example if
you set the setTimeout timeout to 2 seconds, you don't have to wait 2 seconds - the wait
happens elsewhere.
Promises that resolve before the current function ends will be executed right after the current
function.
I find nice the analogy of a rollercoaster ride at an amusement park: the message queue puts
you back in queue with after all the other people in queue, while the job queue is the fastpass
ticket that lets you take another ride right after you finished the previous one.
Example:
foo()
This prints
foo
101
The Event Loop
baz
should be right after foo, before bar
bar
That's a big difference between Promises (and Async/await, which is built on promises) and
plain old asynchronous functions through setTimeout() or other platform APIs.
102
Asynchronous programming and callbacks
Asynchronous means that things can happen independently of the main program flow.
103
Asynchronous programming and callbacks
In the current consumer computers, every program runs for a specific time slot, and then it
stops its execution to let another program continue its execution. This thing runs in a cycle so
fast that's impossible to notice, and we think our computers run many programs
simultaneously, but this is an illusion (except on multiprocessor machines).
Programs internally use interrupts, a signal that's emitted to the processor to gain the attention
of the system.
I won't go into the internals of this, but just keep in mind that it's normal for programs to be
asynchronous, and halt their execution until they need attention, and the computer can
execute other things in the meantime. When a program is waiting for a response from the
network, it cannot halt the processor until the request finishes.
Normally, programming languages are synchronous, and some provide a way to manage
asynchronicity, in the language or through libraries. C, Java, C#, PHP, Go, Ruby, Swift,
Python, they are all synchronous by default. Some of them handle async by using threads,
spawning a new process.
JavaScript
JavaScript is synchronous by default, and is single threaded. This means that code cannot
create new threads and run in parallel.
Lines of code are executed in series, one after another, for example:
const a = 1
const b = 2
const c = a * b
console.log(c)
doSomething()
But JavaScript was born inside the browser, its main job in the beginning was to respond to
user actions, like onClick , onMouseOver , onChange , onSubmit and so on. How could it do this
with a synchronous programming model?
The answer was in its environment. The browser provides a way to do it by providing a set of
APIs that can handle this kind of functionality.
More recently, Node.js introduced a non-blocking I/O environment to extend this concept to file
access, network calls and so on.
Callbacks
104
Asynchronous programming and callbacks
You can't know when a user is going to click a button, so what you do is, you define an event
handler for the click event. This event handler accepts a function, which will be called when
the event is triggered:
document.getElementById('button').addEventListener('click', () => {
//item clicked
})
A callback is a simple function that's passed as a value to another function, and will only be
executed when the event happens. We can do this because JavaScript has first class
functions, which can be assigned to variables and passed around to other functions (called
higher-order functions)
It's common to wrap all your client code in a load event listener on the window object, which
runs the callback function only when the page is ready:
window.addEventListener('load', () => {
//window loaded
//do what you want
})
setTimeout(() => {
// runs after 2 seconds
}, 2000)
XHR requests also accept a callback, in this example by assigning a function to a property that
will be called when a particular even occurs (in this case, the state of the request changes):
105
Asynchronous programming and callbacks
How do you handle errors with callbacks? One very common strategy is to use what Node.js
adopted: the first parameter in any callback function is the error object: error-first callbacks
If there is no error, the object is null . If there is an error, it contains some description of the
error, and other information.
However every callback adds a level of nesting, and when you have lots of callbacks, the code
starts to be complicated very quickly:
window.addEventListener('load', () => {
document.getElementById('button').addEventListener('click', () => {
setTimeout(() => {
items.forEach(item => {
//your code here
})
}, 2000)
})
})
This is just a simple 4-levels code, but I've seen much more levels of nesting and it's not fun.
Alternatives to callbacks
Starting with ES6, JavaScript introduced several features that help us with asynchronous code
that do not involve using callbacks:
106
Asynchronous programming and callbacks
107
Promises
Promises
Promises are one way to deal with asynchronous code in JavaScript, without
writing too many callbacks in your code.
Introduction to promises
How promises work, in brief
Which JS API use promises?
Creating a promise
Consuming a promise
Chaining promises
Example of chaining promises
Handling errors
Cascading errors
Orchestrating promises
Promise.all()
Promise.race()
Common errors
Uncaught TypeError: undefined is not a promise
Introduction to promises
A promise is commonly defined as a proxy for a value that will eventually become
available.
Promises are one way to deal with asynchronous code, without writing too many callbacks in
your code.
Although being around since years, they have been standardized and introduced in ES2015,
and now they have been superseded in ES2017 by async functions.
Async functions use the promises API as their building block, so understanding them is
fundamental even if in newer code you'll likely use async functions instead of promises.
108
Promises
At this point the caller function waits for it to either return the promise in a resolved state, or in
a rejected state, but as you know JavaScript is asynchronous, so the function continues its
execution while the promise does it work.
It's unlikely that in modern JavaScript you'll find yourself not using promises, so let's start
diving right into them.
Creating a promise
The Promise API exposes a Promise constructor, which you initialize using new Promise() :
As you can see the promise checks the done global constant, and if that's true, we return a
resolved promise, otherwise a rejected promise.
Using resolve and reject we can communicate back a value, in the above case we just
return a string, but it could be an object as well.
Consuming a promise
109
Promises
Running checkIfItsDone() will execute the isItDoneYet() promise and will wait for it to
resolve, using the then callback, and if there is any error, it will handle it in the catch
callback.
Chaining promises
A promise can be returned to another promise, creating a chain of promises.
A great example of chaining promises is given by the Fetch API, a layer on top of the
XMLHttpRequest API, which we can use to get a resource and queue a chain of promises to
execute when the resource is fetched.
The Fetch API is a promise-based mechanism, and calling fetch() is equivalent to defining
our own promise using new Promise() .
fetch('/todos.json')
.then(status)
110
Promises
.then(json)
.then((data) => { console.log('Request succeeded with JSON response', data) })
.catch((error) => { console.log('Request failed', error) })
In this example, we call fetch() to get a list of TODO items from the todos.json file found in
the domain root, and we create a chain of promises.
Running fetch() returns a response, which has many properties, and within those we
reference:
response also has a json() method, which returns a promise that will resolve with the
So given those premises, this is what happens: the first promise in the chain is a function that
we defined, called status() , that checks the response status and if it's not a success
response (between 200 and 299), it rejects the promise.
This operation will cause the promise chain to skip all the chained promises listed and will skip
directly to the catch() statement at the bottom, logging the Request failed text along with
the error message.
If that succeeds instead, it calls the json() function we defined. Since the previous promise,
when successful, returned the response object, we get it as an input to the second promise.
In this case we return the data JSON processed, so the third promise receives the JSON
directly:
.then((data) => {
console.log('Request succeeded with JSON response', data)
})
Handling errors
In the example in the previous section we had a catch that was appended to the chain of
promises.
When anything in the chain of promises fails and raises an error or rejects the promise, the
control goes to the nearest catch() statement down the chain.
111
Promises
// or
Cascading errors
If inside the catch() you raise an error, you can append a second catch() to handle it, and
so on.
Orchestrating promises
Promise.all()
If you need to synchronize different promises, Promise.all() helps you define a list of
promises, and execute something when they are all resolved.
Example:
const f1 = fetch('/something.json')
const f2 = fetch('/something2.json')
112
Promises
You are not limited to using fetch of course, any promise is good to go.
Promise.race()
Promise.race() runs when the first of the promises you pass to it resolves, and it runs the
attached callback just once, with the result of the first promise resolved.
Example:
Common errors
Uncaught TypeError: undefined is not a promise
If you get the Uncaught TypeError: undefined is not a promise error in the console, make sure
you use new Promise() instead of just Promise()
113
Template Literals
Template Literals
Introduced in ES2015, aka ES6, Template Literals offer a new way to declare
strings, but also some new interesting constructs which are already widely
popular.
The syntax at a first glance is very simple, just use backticks instead of single or double
quotes:
They are unique because they provide a lot of features that normal strings built with quotes, in
particular:
Multiline strings
Pre-ES6, to create a string spanned over two lines you had to use the \ character at the end
of a line:
This allows to create a string on 2 lines, but it's rendered on just one line:
first part second part
114
Template Literals
To render the string on multiple lines as well, you explicitly need to add \n at the end of each
line, like this:
or
Once a template literal is opened with the backtick, you just press enter to create a new line,
with no special characters, and it's rendered as-is:
string
is awesome!`
First
Second
an easy way to fix this problem is by having an empty first line, and appending the trim()
method right after the closing backtick, which will eliminate any space before the first
character:
const string = `
First
Second`.trim()
Interpolation
115
Template Literals
Template literals provide an easy way to interpolate variables and expressions into strings.
Template tags
Tagged templates is one features that might sound less useful at first for you, but it's actually
used by lots of popular libraries around, like Styled Components or Apollo, the GraphQL
client/server lib, so it's essential to understand how it works.
The styled.button and gql template tags highlighted in those examples are just functions:
this function returns a string, which can be the result of any kind of computation.
literals is an array containing the template literal content tokenized by the expressions
interpolations.
116
Template Literals
literals is an array with two items. The first is something , the string until the first
interpolation, and the second is an empty string, the space betwene the end of the first
interpolation (we only have one) and the end of the string.
`something
another `
`
new line `
`
test`
The function that is passed those values can do anything with them, and this is the power of
this kind feature.
The most simple example is replicating what the string interpolation does, by simply joining
literals and expressions :
117
Template Literals
118
The Set Data Structure
What is a Set
Initialize a Set
Add items to a Set
Check if an item is in the set
Delete an item from a Set by key
Determine the number of items in a Set
Delete all items from a Set
Iterate the items in a Set
Initialize a Set with values
Convert to array
Convert the Set keys into an array
A WeakSet
119
The Set Data Structure
What is a Set
A Set data structure allows to add data to a container.
ECMAScript 6 (also called ES2015) introduced the Set data structure to the JavaScript world,
along with Map
A Set is a collection of objects or primitive types (strings, numbers or booleans), and you can
think of it as a Map where values are used as map keys, with the map value always being a
boolean true.
Initialize a Set
A Set is initialized by calling:
s.add('one')
s.add('two')
A set only stores unique elements, so calling s.add('one') multiple times won't add new
items.
s.has('one') //true
s.has('three') //false
s.delete('one')
120
The Set Data Structure
s.size
s.clear()
The entries() method returns an iterator, which you can use like this:
const i = s.entries()
console.log(i.next())
calling i.next() will return each element as a { value, done = false } object until the
iterator ends, at which point done is true .
for (const k of s) {
console.log(k)
}
121
The Set Data Structure
Convert to array
Convert the Set keys into an array
const a = [...s.keys()]
// or
const a = [...s.values()]
A WeakSet
A WeakSet is a special kind of Set.
In a Set, items are never garbage collected. A WeakSet instead lets all its items be freely
garbage collected. Every key of a WeakSet is an object. When the reference to this object is
lost, the value can be garbage collected.
A WeakSet is generally used by framework-level code, and only exposes these methods:
add()
has()
delete()
122
The Map Data Structure
What is a Map
Before ES6
Enter Map
Add items to a Map
Get an item from a map by key
Delete an item from a map by key
Delete all items from a map
Check if a map contains an item by key
Find the number of items in a map
Initialize a map with values
Map keys
Weird situations you'll almost never find in real life
Iterating over a map
123
The Map Data Structure
What is a Map
A Map data structure allows to associate data to a key.
Before ES6
ECMAScript 6 (also called ES2015) introduced the Map data structure to the JavaScript world,
along with Set
Before its introduction, people generally used objects as maps, by associating some object or
value to a specific key value:
const car = {}
car['color'] = 'red'
car.owner = 'Flavio'
console.log(car['color']) //red
console.log(car.color) //red
console.log(car.owner) //Flavio
console.log(car['owner']) //Flavio
Enter Map
ES6 introduced the Map data structure, providing us a proper tool to handle this kind of data
organization.
124
The Map Data Structure
m.set('color', 'red')
m.set('age', 2)
m.delete('color')
m.clear()
125
The Map Data Structure
Map keys
Just like any value (object, array, string, number) can be used as the value of the key-value
entry of a map item, any value can be used as the key, even objects.
If you try to get a non-existing key using get() out of a map, it will return undefined .
126
The Map Data Structure
Convert to array
Convert the map keys into an array
const a = [...m.keys()]
const a = [...m.values()]
WeakMap
A WeakMap is a special kind of map.
In a Map, items are never garbage collected. A WeakMap instead lets all its items be freely
garbage collected. Every key of a WeakMap is an object. When the reference to this object is
lost, the value can be garbage collected.
1. you cannot iterate over the keys or values (or key-values) of a WeakMap
2. you cannot clear all items from a WeakMap
3. you cannot check its size
A WeakMap exposes those methods, which are equivalent to the Map ones:
get(k)
set(k, v)
has(k)
delete(k)
127
The Map Data Structure
The use cases of a WeakMap are less evident than the ones of a Map, and you might never
find the need for them, but essentially it can be used to build a memory-sensitive cache that is
not going to interfere with garbage collection, or for careful encapsualtion and information
hiding.
128
Loops and Scope
There is one feature of JavaScript that might cause a few headaches to developers, related to
loops and scoping.
const operations = []
It basically iterates and for 5 times it adds a function to an array called operations. This
function simply console logs the loop index variable i .
0
1
2
3
4
5
5
5
5
5
129
Loops and Scope
var i;
const operations = []
so, in the for-of loop, i is still visible, it's equal to 5 and every reference to i in the function
is going to use this value.
The simplest solution is to use let declarations. Introduced in ES2015, they are a great help
in avoiding some of the weird things about var declarations.
Simply changing var to let in the loop variable is going to work fine:
const operations = []
0
1
2
3
4
How is this possible? This works because on every loop iteration i is created as a new
variable each time, and every function added to the operations array gets its own copy of i .
Keep in mind you cannot use const in this case, because there would be an error as for
tries to assign a new value in the second iteration.
130
Loops and Scope
Another way to solve this problem was very common in pre-ES6 code, and it is called
Immediately Invoked Function Expression (IIFE).
In this case you can wrap the entire function and bind i to it. Since in this way you're
creating a function that immediately executes, you return a new function from it, so we can
execute it later:
const operations = []
131
Timers
Timers
When writing JavaScript code, you might want to delay the execution of a
function. Learn how to use setTimeout and setInterval to schedule functions
in the future
setTimeout()
Zero delay
setInterval()
Recursive setTimeout
Node.js
setTimeout()
When writing JavaScript code, you might want to delay the execution of a function.
This is the job of setTimeout . You specify a callback function to execute later, and a value
expressing how later you want it to run, in milliseconds:
setTimeout(() => {
// runs after 2 seconds
}, 2000)
setTimeout(() => {
// runs after 50 milliseconds
}, 50)
This syntax defines a new function. You can call whatever other function you want in there, or
you can pass an existing function name, and a set of parameters:
132
Timers
// do something
}
setTimeout returns the timer id. This is generally not used, but you can store this id, and clear
// I changed my mind
clearTimeout(id)
Zero delay
If you specify the timeout delay to 0 , the callback function will be executed as soon as
possible, but after the current function execution:
setTimeout(() => {
console.log('after ')
}, 0)
This is especially useful to avoid blocking the CPU on intensive tasks, and let other functions
be executed while performing a heavy calculation, by queuing functions in the scheduler.
Some browsers (IE and Edge) implement a setImmediate() method that does this same
exact functionality, but it's not standard and unavailable on other browsers. But it's a
standard function in Node.js.
setInterval()
callback function once, it will run it forever, at the specific time interval you specify (in
milliseconds):
setInterval(() => {
// runs every 2 seconds
}, 2000)
133
Timers
The function above runs every 2 seconds unless you tell it to stop, using clearInterval ,
passing it the interval id that setInterval returned:
clearInterval(id)
It's common to call clearInterval inside the setInterval callback function, to let it auto-
determine if it should run again or stop. For example this code runs something unless
App.somethingIWait has the value arrived :
// otherwise do things
}
}, 100)
Recursive setTimeout
setInterval starts a function every n milliseconds, without any consideration about when a
If a function takes always the same amount of time, it's all fine:
Maybe the function takes different execution times, depending on network conditions for
example:
134
Timers
To avoid this, you can schedule a recursive setTimeout to be called when the callback function
finishes:
setTimeout(myFunction, 1000)
}
setTimeout(
myFunction()
}, 1000)
Node.js
setTimeout and setInterval are also available in Node.js, through the Timers module.
Node.js also provides setImmediate() , which is equivalent to using setTimeout(() => {}, 0) ,
mostly used to work with the Node.js Event Loop.
135
this in JavaScript
this in JavaScript
`this` is a value that has different values depending on where it's used. Not
knowing this tiny detail of JavaScript can cause a lot of headaches, so it's
worth taking 5 minutes to learn all the tricks
this is a value that has different values depending on where it's used.
Not knowing this tiny detail of JavaScript can cause a lot of headaches, so it's worth taking 5
minutes to learn all the tricks.
Notice I mentioned strict mode. If strict mode is disabled (the default state if you don't explicitly
add 'use strict' on top of your file ), you are in the so-called sloppy mode, and this -
unless some specific cases mentioned here below - has the value of the global object.
this in methods
136
this in JavaScript
Here's one:
const car = {
maker: 'Ford',
model: 'Fiesta',
drive() {
console.log(`Driving a ${this.maker} ${this.model} car!`)
}
}
car.drive()
//Driving a Ford Fiesta car!
In this case, using a regular function, this is automatically bound to the object.
Note: the above method declaration is the same as drive: function() { ..., but shorter:
const car = {
maker: 'Ford',
model: 'Fiesta',
drive: function() {
console.log(`Driving a ${this.maker} ${this.model} car!`)
}
}
const car = {
maker: 'Ford',
model: 'Fiesta'
}
car.drive = function() {
console.log(`Driving a ${this.maker} ${this.model} car!`)
}
car.drive()
//Driving a Ford Fiesta car!
An arrow function does not work in the same way, as it's lexically bound:
const car = {
maker: 'Ford',
model: 'Fiesta',
drive: () => {
console.log(`Driving a ${this.maker} ${this.model} car!`)
}
}
137
this in JavaScript
car.drive()
//Driving a undefined undefined car!
It's simply not possible due to the way they work. this is lexically bound, which means its
value is derived from the context where they are defined.
const car = {
maker: 'Ford',
model: 'Fiesta'
}
drive()
//Driving a Ford Fiesta car!
You could also bind an existing object method to remap its this value:
const car = {
maker: 'Ford',
model: 'Fiesta',
drive() {
console.log(`Driving a ${this.maker} ${this.model} car!`)
}
}
const anotherCar = {
maker: 'Audi',
model: 'A4'
}
car.drive.bind(anotherCar)()
//Driving a Audi A4 car!
138
this in JavaScript
const car = {
maker: 'Ford',
model: 'Fiesta'
}
drive.call(car, 100)
//Driving a Ford Fiesta car at 100 km/h!
drive.apply(car, [100])
//Driving a Ford Fiesta car at 100 km/h!
The first parameter you pass to call() or apply() is always bound to this . The difference
between call() and apply() is just that the second one wants an array as the arguments list,
while the first accepts a variable number of parameters, which passes as function arguments.
document.querySelector('#button').addEventListener('click', function(e) {
console.log(this) //HTMLElement
}
document.querySelector('#button').addEventListener(
'click',
function(e) {
console.log(this) //Window if global, or your context
}.bind(this)
)
139
JavaScript Strict Mode
Strict Mode is an ES5 feature, and it's a way to make JavaScript behave in a better way.
And in a different way, as enabling Strict Mode changes the semantics of the JavaScript
language.
It's really important to know the main differences between JavaScript code in strict mode, and
"normal" JavaScript, which is often referred as sloppy mode.
Strict Mode mostly removes functionality that was possible in ES3, and deprecated since ES5
(but not removed because of backwards compatibility requirements)
140
JavaScript Strict Mode
Strict mode is optional. As with every breaking change in JavaScript, we can't simply change
how the language behaves by default, because that would break gazillions of JavaScript
around, and JavaScript puts a lot of effort into making sure 1996 JavaScript code still works
today. It's a key of its success.
So we have the 'use strict' directive we need to use to enable Strict Mode.
You can put it at the beginning of a file, to apply it to all the code contained in the file:
'use strict'
//...
You can also enable Strict Mode for an individual function, by putting 'use strict' at the
beginning of the function body:
function hello() {
'use strict'
return 'hey'
}
This is useful when operating on legacy code, where you don't have the time to test or the
confidence to enable strict mode on the whole file.
;(function() {
variable = 'hey'
})()(() => {
name = 'Flavio'
})()
variable //'hey'
name //'Flavio'
Turning on Strict Mode, an error is raised if you try to do what we did above:
141
JavaScript Strict Mode
;(function() {
'use strict'
variable = 'hey'
})()(() => {
'use strict'
myname = 'Flavio'
})()
Assignment errors
JavaScript silently fails some conversion errors.
142
JavaScript Strict Mode
const car = {}
Object.defineProperty(car, 'color', { value: 'blue', writable: false })
In strict mode, you can't override this value, while in sloppy mode that's possible:
const car = {
get color() {
return 'blue'
}
}
car.color = 'red'(
//ok
() => {
'use strict'
143
JavaScript Strict Mode
car.color = 'yellow' //TypeError: Cannot set property color of #<Object> which has onl
y a getter
}
)()
() => {
'use strict'
car.owner = 'Flavio' //TypeError: Cannot add property owner, object is not extensible
}
)()
Also, sloppy mode allows to set properties on primitive values, without failing, but also without
doing nothing at all:
true.false = ''(
//''
1
).name =
'xxx' //'xxx'
var test = 'test' //undefined
test.testing = true //true
test.testing //undefined
;(() => {
'use strict'
true.false = ''(
//TypeError: Cannot create property 'false' on boolean 'true'
1
).name =
'xxx' //TypeError: Cannot create property 'name' on number '1'
'test'.testing = true //TypeError: Cannot create property 'testing' on string 'test'
})()
Deletion errors
In sloppy mode, if you try to delete a property that you cannot delete, JavaScript simply returns
false, while in Strict Mode, it raises a TypeError:
delete Object.prototype(
144
JavaScript Strict Mode
//false
() => {
'use strict'
delete Object.prototype //TypeError: Cannot delete property 'prototype' of function Ob
ject() { [native code] }
}
)()
(function(a, a, b) {
console.log(a, b)
})(1, 2, 3)
//2 3
(function(a, a, b) {
'use strict'
console.log(a, b)
})(1, 2, 3)
//Uncaught SyntaxError: Duplicate parameter name not allowed in this context
((a, a, b) => {
console.log(a, b)
})(1, 2, 3)
//Uncaught SyntaxError: Duplicate parameter name not allowed in this context
Octal syntax
Octal syntax in Strict Mode is disabled. By default, prepending a 0 to a number compatible
with the octal numeric format makes it (sometimes confusingly) interpreted as an octal
number:
(() => {
console.log(010)
})()
//8
(() => {
'use strict'
console.log(010)
})()
//Uncaught SyntaxError: Octal literals are not allowed in strict mode.
145
JavaScript Strict Mode
You can still enable octal numbers in Strict Mode using the 0oXX syntax:
;(() => {
'use strict'
console.log(0o10)
})()
//8
Removed with
Strict Mode disables the with keyword, to remove some edge cases and allow more
optimization at the compiler level.
146
JavaScript Immediately-invoked Function Expressions (IIFE)
IIFEs are very useful because they don't pollute the global object, and they are a simple
way to isolate variables declarations.
;(function() {
/* */
})()
147
JavaScript Immediately-invoked Function Expressions (IIFE)
;(() => {
/* */
})()
We basically have a function defined inside parentheses, and then we append () to execute
that function: (/* function */)() .
Those wrapping parentheses are actually what make our function, internally, be considered an
expression. Otherwise, the function declaration would be invalid, because we didn't specify
any name:
Function declarations want a name, while function expressions do not require it.
You could also put the invoking parentheses inside the expression parentheses, there is no
difference, just a styling preference:
(function() {
/* */
}())
(() => {
/* */
}())
;-(function() {
/* */
})() +
(function() {
/* */
})()
148
JavaScript Immediately-invoked Function Expressions (IIFE)
~(function() {
/* */
})()
!(function() {
/* */
})()
Named IIFE
An IIFE can also be named regular functions (not arrow functions). This does not change the
fact that the function does not "leak" to the global scope, and it cannot be invoked again after
its execution:
;(function doSomething() {
/* */
})()
;(function() {
/* */
})()
This prevents issues when blindly concatenating two JavaScript files. Since JavaScript does
not require semicolons, you might concatenate with a file with some statements in its last line
that causes a syntax error.
This problem is essentially solved with "smart" code bundlers like webpack.
149
Async and Await
Introduction
Why were async/await introduced?
How it works
A quick example
Promise all the things
The code is much simpler to read
Multiple async functions in series
Easier debugging
Introduction
JavaScript evolved in a very short time from callbacks to promises (ES2015), and since
ES2017 asynchronous JavaScript is even simpler with the async/await syntax.
Async functions are a combination of promises and generators, and basically they are a higher
level abstraction over promises. Let me repeat: async/await is built on promises.
When Promises were introduced in ES2015, they were meant to solve a problem with
asynchronous code, and they did, but over the 2 years that separated ES2015 and ES2017, it
was clear that promises could not be the final solution.
Promises were introduced to solve the famous callback hell problem, but they introduced
complexity on their own, and syntax complexity.
They were good primitives around which a better syntax could be exposed to the developers,
so when the time was right we got async functions.
They make the code look like it's synchronous, but it's asynchronous and non-blocking behind
the scenes.
150
Async and Await
How it works
An async function returns a promise, like in this example:
When you want to call this function you prepend await , and the calling code will stop until
the promise is resolved or rejected. One caveat: the client function must be defined as
async . Here's an example:
A quick example
This is a simple example of async/await used to run a function asynchronously:
console.log('Before')
doSomething()
console.log('After')
The above code will print the following to the browser console:
Before
After
I did something //after 3s
151
Async and Await
Prepending the async keyword to any function means that the function will return a promise.
Even if it's not doing so explicitly, it will internally make it return a promise.
And this is a very simple example, the major benefits will arise when the code is much more
complex.
For example here's how you would get a JSON resource, and parse it, using promises:
getFirstUserData()
152
Async and Await
getFirstUserData()
watchOverSomeoneWatchingSomeoneDoingSomething().then((res) => {
console.log(res)
})
Will print:
Easier debugging
Debugging promises is hard because the debugger will not step over asynchronous code.
Async/await makes this very easy, because to the compiler it's just like synchronous code.
153
Dates in JavaScript
Dates in JavaScript
Working with dates in JavaScript can be complicated. Learn all the quirks
and how to use them.
Introduction
Working with dates can be complicated. No matter the technology, developers do feel the
pain.
154
Dates in JavaScript
This article does not talk about Moment.js, which I believe it's the best library out there to
handle dates, and you should almost always use that when working with dates.
new Date()
Internally, dates are expressed in milliseconds since Jan 1st 1970 (UTC). This date is
important because as far as computers are concerned, that's where it all began.
155
Dates in JavaScript
You might be familiar with the UNIX timestamp: that represents the number of seconds that
passed since that famous date.
If we pass 0 we'd get a Date object that represents the time at Jan 1st 1970 (UTC):
new Date(0)
If we pass a string rather than a number, then the Date object uses the parse method to
determine which date you are passing. Examples:
new Date('2018-07-22')
new Date('2018-07') //July 1st 2018, 00:00:00
new Date('2018') //Jan 1st 2018, 00:00:00
new Date('07/22/2018')
new Date('2018/07/22')
new Date('2018/7/22')
new Date('July 22, 2018')
new Date('July 22, 2018 07:22:13')
new Date('2018-07-22 07:22:13')
new Date('2018-07-22T07:22:13')
new Date('25 March 2018')
new Date('25 Mar 2018')
new Date('25 March, 2018')
new Date('March 25, 2018')
new Date('March 25 2018')
new Date('March 2018') //Mar 1st 2018, 00:00:00
new Date('2018 March') //Mar 1st 2018, 00:00:00
new Date('2018 MARCH') //Mar 1st 2018, 00:00:00
new Date('2018 march') //Mar 1st 2018, 00:00:00
There's lots of flexibility here. You can add, or omit, the leading zero in months or days.
Be careful with the month/day position, or you might end up with the month being
misinterpreted as the day.
Date.parse('2018-07-22')
Date.parse('2018-07') //July 1st 2018, 00:00:00
Date.parse('2018') //Jan 1st 2018, 00:00:00
Date.parse('07/22/2018')
156
Dates in JavaScript
Date.parse('2018/07/22')
Date.parse('2018/7/22')
Date.parse('July 22, 2018')
Date.parse('July 22, 2018 07:22:13')
Date.parse('2018-07-22 07:22:13')
Date.parse('2018-07-22T07:22:13')
Date.parse will return a timestamp (in milliseconds) rather than a Date object.
You can also pass a set of ordered values that represent each part of a date: the year, the
month (starting from 0), the day, the hour, the minutes, seconds and milliseconds:
The minimum should be 3 parameters, but most JavaScript engines also interpret less than
these:
new Date(2018, 6) //Sun Jul 01 2018 00:00:00 GMT+0200 (Central European Summer Time)
new Date(2018) //Thu Jan 01 1970 01:00:02 GMT+0100 (Central European Standard Time)
In any of these cases, the resulting date is relative to the timezone of your computer. This
means that two different computers might output a different value for the same date
object.
JavaScript, without any information about the timezone, will consider the date as UTC, and will
automatically perform a conversion to the current computer timezone.
Timezones
When initializing a date you can pass a timezone, so the date is not assumed UTC and then
converted to your local timezone.
You can specify a timezone by adding it in +HOURS format, or by adding the timezone name
wrapped in parentheses:
157
Dates in JavaScript
If you specify a wrong timezone name in the parentheses, JavaScript will default to UTC
without complaining.
If you specify a wrong numeric format, JavaScript will complain with an "Invalid Date" error.
date.toString() // "Sun Jul 22 2018 07:22:13 GMT+0200 (Central European Summer Time)"
date.toTimeString() //"07:22:13 GMT+0200 (Central European Summer Time)"
date.toUTCString() //"Sun, 22 Jul 2018 05:22:13 GMT"
date.toDateString() //"Sun Jul 22 2018"
date.toISOString() //"2018-07-22T05:22:13.000Z" (ISO 8601 format)
date.toLocaleString() //"22/07/2018, 07:22:13"
date.toLocaleTimeString() //"07:22:13"
date.getTime() //1532236933000
date.getTime() //1532236933000
date.getDate() //22
date.getDay() //0 (0 means sunday, 1 means monday..)
date.getFullYear() //2018
date.getMonth() //6 (starts from 0)
date.getHours() //7
date.getMinutes() //22
date.getSeconds() //13
date.getMilliseconds() //0 (not specified)
date.getTime() //1532236933000
date.getTimezoneOffset() //-120 (will vary depending on where you are and when you check -
this is CET during the summer). Returns the timezone difference expressed in minutes
There are equivalent UTC versions of these methods, that return the UTC value rather than
the values adapted to your current timezone:
date.getUTCDate() //22
158
Dates in JavaScript
Editing a date
A Date object offers several methods to edit a date value:
date.setDate(newValue)
date.setDay(newValue)
date.setFullYear(newValue) //note: avoid setYear(), it's deprecated
date.setMonth(newValue)
date.setHours(newValue)
date.setMinutes(newValue)
date.setSeconds(newValue)
date.setMilliseconds(newValue)
date.setTime(newValue)
date.setTimezoneOffset(newValue)
setDay and setMonth start numbering from 0, so for example March is month 2.
Fun fact: those methods "overlap", so if you, for example, set date.setHours(48) , it will
increment the day as well.
Good to know: you can add more than one parameter to setHours() to also set minutes,
seconds and milliseconds: setHours(0, 0, 0, 0) - the same applies to setMinutes and
setSeconds .
date.setUTCDate(newalue)
date.setUTCDay(newValue)
date.setUTCFullYear(newValue)
date.setUTCMonth(newValue)
date.setUTCHours(newValue)
date.setUTCMinutes(newValue)
date.setUTCSeconds(newValue)
date.setUTCMilliseconds(newValue)
159
Dates in JavaScript
Date.now()
instead of
new Date().getTime()
new Date(2018, 6, 40) //Thu Aug 09 2018 00:00:00 GMT+0200 (Central European Summer Time)
The same goes for months, hours, minutes, seconds and milliseconds.
It's exposed by the Intl object, which also helps localizing numbers, strings and currencies.
// "12/22/2017"
const date = new Date('July 22, 2018 07:22:13')
new Intl.DateTimeFormat().format(date) //"22/07/2018" in my locale
160
Dates in JavaScript
Intl.DateTimeFormat method takes an optional parameter that lets you customize the output.
const options = {
year: 'numeric',
month: 'numeric',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
second: 'numeric'
}
In the same way you can check if two dates are equal:
Keep in mind that getTime() returns the number of milliseconds, so you need to factor in time
in the comparison. July 10, 2018 07:22:13 is not equal to new July 10, 2018 . In this case
you can use setHours(0, 0, 0, 0) to reset the time.
161
Math operators
Math operators
Performing math operations and calculus is a very common thing to do with
any programming language. JavaScript offers several operators to help us
work with numbers
Performing math operations and calculus is a very common thing to do with any programming
language.
Operators
Arithmetic operators
Addition (+)
const three = 1 + 2
const four = three + 1
The + operator also serves as string concatenation if you use strings, so pay attention:
const three = 1 + 2
three + 1 // 4
'three' + 1 // three1
Subtraction (-)
const two = 4 - 2
Division (/)
Returns the quotient of the first operator and the second:
If you divide by zero, JavaScript does not raise any error but returns the Infinity value (or -
Infinity if the value is negative).
162
Math operators
1 / 0 //Infinity
-1 / 0 //-Infinity
Remainder (%)
The remainder is a very useful calculation in many use cases:
A reminder by zero is always NaN , a special value that means "Not a Number":
1 % 0 //NaN
-1 % 0 //NaN
Multiplication (*)
1 * 2 //2
-1 * 2 //-2
Exponentiation (**)
Raise the first operand to the power second operand
1 ** 2 //1
2 ** 1 //2
2 ** 2 //4
2 ** 8 //256
8 ** 2 //64
Unary operators
Increment (++)
Increment a number. This is a unary operator, and if put before the number, it returns the
value incremented.
If put after the number, it returns the original value, then increments it.
let x = 0
x++ //0
x //1
++x //2
163
Math operators
Decrement (--)
Works like the increment operator, except it decrements the value.
let x = 0
x-- //0
x //-1
--x //-2
let x = 2
-x //-2
x //2
let x = 2
+x //2
x = '2'
+x //2
x = '2a'
+x //NaN
Assignment shortcuts
The regular assignment operator, = , has several shortcuts for all the arithmetic operators
which let you combine assignment, assigning to the first operand the result of the operations
with the second operand.
They are:
+= : addition assignment
-= : subtraction assignment
*= : multiplication assignment
164
Math operators
/= : division assignment
%= : remainder assignment
Examples:
const a = 0
a += 5 //a === 5
a -= 2 //a === 3
a *= 2 //a === 6
a /= 2 //a === 3
a %= 2 //a === 1
Precedence rules
Every complex statement will introduce precedence problems.
Take this:
const a = 1 * 2 + 5 / 2 % 2
The result is 2.5, but why? What operations are executed first, and which need to wait?
Some operations have more precedence than the others. The precedence rules are listed in
this table:
Operator Description
- + ++ -- unary operators, increment and decrement
* / % multiply/divide
+ - addition/subtraction
= += -= *= /= %= **= assignments
Operations on the same level (like + and - ) are executed in the order they are found
const a = 1 * 2 + 5 / 2 % 2
const a = 1 * 2 + 5 / 2 % 2
const a = 2 + 2.5 % 2
const a = 2 + 0.5
const a = 2.5
165
Math operators
166
The Math object
Constants
Item Description
Math.E The constant e, base of the natural logarithm (means ~2.71828)
Math.LN10 The constant that represents the base e (natural) logarithm of 10
Math.LN2 The constant that represents the base e (natural) logarithm of 2
Math.LOG10E The constant that represents the base 10 logarithm of e
Math.LOG2E The constant that represents the base 2 logarithm of e
Math.PI The π constant (~3.14159)
Math.SQRT1_2 The constant that represents the reciprocal of the square root of 2
Math.SQRT2 The constant that represents the square root of 2
Functions
All those functions are static. Math cannot be instantiated.
Math.abs()
Returns the absolute value of a number
Math.abs(2.5) //2.5
Math.abs(-2.5) //2.5
Math.acos()
Returns the arccosine of the operand
167
The Math object
Math.acos(0.8) //0.6435011087932843
Math.asin()
Returns the arcsine of the operand
Math.asin(0.8) //0.9272952180016123
Math.atan()
Returns the arctangent of the operand
Math.atan(30) //1.5374753309166493
Math.atan2()
Returns the arctangent of the quotient of its arguments.
Math.ceil()
Rounds a number up
Math.ceil(2.5) //3
Math.ceil(2) //2
Math.ceil(2.1) //3
Math.ceil(2.99999) //3
Math.cos()
Return the cosine of an angle expressed in radiants
Math.cos(0) //1
Math.cos(Math.PI) //-1
Math.exp()
Return the value of Math.E multiplied per the exponent that's passed as argument
168
The Math object
Math.exp(1) //2.718281828459045
Math.exp(2) //7.38905609893065
Math.exp(5) //148.4131591025766
Math.floor()
Rounds a number down
Math.ceil(2.5) //2
Math.ceil(2) //2
Math.ceil(2.1) //2
Math.ceil(2.99999) //2
Math.log()
Return the base e (natural) logarithm of a number
Math.log(10) //2.302585092994046
Math.log(Math.E) //1
Math.max()
Return the highest number in the set of numbers passed
Math.max(1,2,3,4,5) //5
Math.max(1) //1
Math.min()
Return the smallest number in the set of numbers passed
Math.max(1,2,3,4,5) //1
Math.max(1) //1
Math.pow()
Return the first argument raised to the second argument
Math.pow(1, 2) //1
Math.pow(2, 1) //2
Math.pow(2, 2) //4
Math.pow(2, 4) //16
169
The Math object
Math.random()
Returns a pseudorandom number between 0.0 and 1.0
Math.random() //0.9318168241227056
Math.random() //0.35268950194094395
Math.round()
Rounds a number to the nearest integer
Math.round(1.2) //1
Math.round(1.6) //2
Math.sin()
Calculates the sin of an angle expressed in radiants
Math.sin(0) //0
Math.sin(Math.PI) //1.2246467991473532e-16)
Math.sqrt()
Return the square root of the argument
Math.sqrt(4) //2
Math.sqrt(16) //4
Math.sqrt(5) //2.23606797749979
Math.tan()
Calculates the tangent of an angle expressed in radiants
Math.tan(0) //0
Math.tan(Math.PI) //-1.2246467991473532e-16
170
Unicode in JavaScript
Unicode in JavaScript
Learn how to work with Unicode in JavaScript, learn what Emojis are made
of, ES6 improvements and some pitfalls of handling Unicode in JS
171
Unicode in JavaScript
How do you specify another encoding, in particular UTF-8, the most common file encoding on
the web?
If the file contains a BOM character, that has priority on determining the encoding. You can
read many different opinions online, some say a BOM in UTF-8 is discouraged, and some
editors won't even add it.
... Use of a BOM is neither required nor recommended for UTF-8, but may be
encountered in contexts where UTF-8 data is converted from other encoding forms that
use a BOM or where the BOM is used as a UTF-8 signature.
In HTML5 browsers are required to recognize the UTF-8 BOM and use it to detect the
encoding of the page, and recent versions of major browsers handle the BOM as
expected when used for UTF-8 encoded pages. --
https://www.w3.org/International/questions/qa-byte-order-mark
If the file is fetched using HTTP (or HTTPS), the Content-Type header can specify the
encoding:
If this is not set, the fallback is to check the charset attribute of the script tag:
...
<head>
<meta charset="utf-8">
</head>
...
The charset attribute in both cases is case insensitive (see the spec)
Public libraries should generally avoid using characters outside the ASCII set in their code, to
avoid it being loaded by users with an encoding that is different than their original one, and
thus create issues.
172
Unicode in JavaScript
JavaScript strings are all UTF-16 sequences, as the ECMAScript standard says:
When a String contains actual textual data, each element is considered to be a single
UTF-16 code unit.
Notice that while both generate an accented e, they are two different strings, and s2 is
considered to be 2 characters long:
s1.length //1
s2.length //2
And when you try to select that character in a text editor, you need to go through it 2 times, as
the first time you press the arrow key to select it, it just selects half element.
You can write a string combining a unicode character with a plain char, as internally it's
actually the same thing:
Normalization
Unicode normalization is the process of removing ambiguities in how a character can be
represented, to aid in comparing strings, for example.
173
Unicode in JavaScript
ES6/ES2015 introduced the normalize() method on the String prototype, so we can simply do:
Emojis
Emojis are fun, and they are Unicode characters, and as such they are perfectly valid to be
used in strings:
Emojis are part of the astral planes, outside of the first Basic Multilingual Plane (BMP), and
since those points outside BMP cannot be represented in 16 bits, JavaScript needs to use a
combination of 2 characters to represent them
Some emojis are also created by combining together other emojis. You can find those by
looking at this list https://unicode.org/emoji/charts/full-emoji-list.html and notice the ones that
have more than one item in the unicode symbol column.
' ❤ '.length
You'll get 8 in return, as length counts the single Unicode code points.
174
Unicode in JavaScript
And curiously, pasting this emoji in a password field it's counted 8 times, possibly making it a
valid password in some systems.
Note that emojis that are built by combining other emojis will still give a bad count:
If the string has combining marks however, this still will not give the right count. Check this
Glitch https://glitch.com/edit/#!/node-unicode-ignore-marks-in-length as an example.
(you can generate your own weird text with marks here:
https://lingojam.com/WeirdTextGenerator)
Length is not the only thing to pay attention. Also reversing a string is error prone if not
handled correctly.
175
Unicode in JavaScript
'\u{XXXXX}'
The dog symbol, which is U+1F436 , can be represented as \u{1F436} instead of having to
combine two unrelated Unicode code points, like we showed before: \uD83D\uDC36 .
But length calculation still does not work correctly, because internally it's converted to the
surrogate pair shown above.
'\x61' // a
'\x2A' // *
This will only work from \x00 to \xFF , which is the set of ASCII characters.
176
Functional Programming
Functional Programming
Getting started with the main concepts of Functional Programming in the
JavaScript Programming Language
Object.assign()
concat()
filter()
Purity
Data Transformations
Array.map()
Array.reduce()
Recursion
Composition
Composing in plain JS
Composing with the help of lodash
Haskell, Clojure and Scala are some of the most popular purely functional programming
languages.
177
Functional Programming
Functional Programming is not a new concept, actually its roots go back o the 1930's when
lamda calculus was born, and has influenced many programming languages.
FP has been gaining a lot of momentum lately, so it's the perfect time to learn about it.
In this course I'll introduce the main concepts of Functional Programming, by using in the code
examples JavaScript.
const obj = {
f(m) {
console.log(m)
}
}
obj.f('Test')
as well as to arrays:
const a = [
m => console.log(m)
]
a[0]('Test')
178
Functional Programming
}
const f = createF()
f('Test')
Declarative programming
You may have heard the term "declarative programming".
Declarative vs Imperative
An imperative approach is when you tell the machine (in general terms), the steps it needs to
take to get a job done.
A declarative approach is when you tell the machine what you need to do, and you let it figure
out the details.
You start thinking declarative when you have enough level of abstraction to stop reasoning
about low level constructs, and think more at a higher UI level.
One might argue that C programming is more declarative than Assembly programming, and
that's true.
HTML is declarative, so if you've been using HTML since 1995, you've actually being building
declarative UIs since 20+ years.
For example a declarative programming approach is to avoid using loops and instead use
functional programming constructs like map , reduce and filter , because your programs
are more abstract and less focused on telling the machine each step of processing.
179
Functional Programming
Immutability
In functional programming data never changes. Data is immutable.
A variable can never be changed. To update its value, you create a new variable.
Instead of changing an array, to add a new item you create a new array by concatenating the
old array, plus the new item.
const
This is why the ES2015 const is so widely used in modern JavaScript, which embraces
functional programming concepts: to enforce immutability on variables.
Object.assign()
ES2015 also gave us Object.assign() , which is key to creating objects:
concat()
To append an item to an array in JavaScript we generally use the push() method on an
array, but that method mutates the original array, so it's not FP-ready.
const a = [1, 2]
const b = [1, 2].concat(3)
// b = [1, 2, 3]
const c = [...a, 3]
// c = [1, 2, 3]
filter()
The same goes for removing an item from an array: instead of using pop() and splice() ,
which modify the original array, use array.filter() :
180
Functional Programming
Purity
A pure function:
never changes any of the parameters that get passed to it by reference (in JS, objects
and arrays): they should be considered immutable. It can of course change any parameter
copied by value
the return value of a pure function is not influenced by anything else than its input
parameters: passing the same parameters always result in the same output
during its execution, a pure function does not change anything outside of it
Data Transformations
Since immutability is such an important concept and a foundation of functional programming,
you might ask how can data change.
Array.map()
Calling Array.map() on an array will create a new array with the result of a function executed
on every item of the original array:
const a = [1, 2, 3]
const b = a.map((v, k) => v * k)
// b = [0, 2, 6]
Array.reduce()
Calling Array.reduce() on an array allows us to transform that array on anything else,
including a scalar, a function, a boolean, an object.
You pass a function that processes the result, and a starting point:
const a = [1, 2, 3]
181
Functional Programming
Recursion
Recursion is a key topic in functional programming. when a function calls itself, it's called a
recursive function.
The classic example of recursion is the Fibonacci sequence (N = (N-1 + N-2)) calculation, here
in its 2^N totally inefficient (but nice to read) solution:
Composition
Composition is another key topic of Functional Programming, a good reason to put it into the
"key topics" list.
Composing in plain JS
A very common way to compose functions in plain JavaScript is to chain them:
obj.doSomething()
.doSomethingElse()
or, also very widely used, by passing a function execution into a function:
obj.doSomething(doThis())
182
Functional Programming
with an argument, each function inherits the argument from the preceding function
return value. Notice how we don't need to store intermediate values anywhere.
183
Regular Expressions
Regular Expressions
Learn everything about JavaScript Regular Expressions with this brief guide
that summarizes the most important concepts and shows them off with
examples
{n}
{n,m}
Optional items
184
Regular Expressions
Groups
Capturing Groups
Optional groups
Reference matched groups
Named Capturing Groups
Using match and exec without groups
Noncapturing Groups
Flags
Inspecting a regex
Escaping
String boundaries
Replacing using Regular Expressions
Greediness
Lookaheads: match a string depending on what follows it
Lookbehinds: match a string depending on what precedes it
Regular Expressions and Unicode
Unicode property escapes
Examples
Extract a number from a string
Match an email address
Capture text between double quotes
Get the content inside an HTML tag
Almost every programming language implements regular expressions. There are small
differences between each implementation, but the general concepts apply almost everywhere.
Regular Expressions date back to the 1950s, when it was formalized as a conceptual search
pattern for string processing algorithms.
Implemented in UNIX tools like grep, sed, and in popular text editors, regexes grew in
popularity and were introduced in the Perl programming language, and later in many others.
185
Regular Expressions
JavaScript, among with Perl, is one of the programming languages that have regular
expressions support directly built in the language.
Cryptic regular expressions are hard to write, hard to read, and hard to maintain/modify.
But sometimes a regular expression is the only sane way to perform some string
manipulation, so it's a very valuable tool in your pocket.
This tutorial aims to introduce you to JavaScript Regular Expressions in a simple way, and
give you all the information to read and create regular expressions.
The rule of thumb is that simple regular expressions are simple to read and write, while
complex regular expressions can quickly turn into a mess if you don't deeply grasp the
basics.
You know that JavaScript has object literals and array literals? It also has regex literals.
In the example above, hey is called the pattern. In the literal form it's delimited by forward
slashes, while with the object constructor, it's not.
This is the first important difference between the two forms, but we'll see others later.
186
Regular Expressions
The regular expression we defined as re1 above is a very simple one. It searches the string
hey , without any limitation: the string can contain lots of text, and hey in the middle, and the
regex is satisfied. It could also contains just hey , and it will be satisfied as well.
You can test the regex using RegExp.test(String) , which returns a boolean:
re1.test('hey') //✅
re1.test('blablabla hey blablabla') //✅
re1.test('he') //❌
re1.test('blablabla') //❌
In the above example we just checked if "hey" satisfies the regular expression pattern stored
in re1 .
This is the simplest it can be, but you already know lots of concepts about regexes.
Anchoring
/hey/
If you want to match strings that start with hey , use the ^ operator:
/^hey/.test('hey') //✅
/^hey/.test('bla hey') //❌
If you want to match strings that end with hey , use the $ operator:
/hey$/.test('hey') //✅
/hey$/.test('bla hey') //✅
/hey$/.test('hey you') //❌
Combine those, and match strings that exactly match hey , and just that string:
/^hey$/.test('hey') //✅
To match a string that starts with a substring and ends with another, you can use .* , which
matches any character repeated 0 or more times:
187
Regular Expressions
These regexes match strings that contain at least one of the characters in those ranges:
/[a-z]/.test('a') //✅
/[a-z]/.test('1') //❌
/[a-z]/.test('A') //❌
/[a-c]/.test('d') //❌
/[a-c]/.test('dc') //✅
/[A-Za-z0-9]/
/[A-Za-z0-9]/.test('a') //✅
/[A-Za-z0-9]/.test('1') //✅
/[A-Za-z0-9]/.test('A') //✅
/^[A-Za-z0-9]$/
/^[A-Za-z0-9]$/.test('A') //✅
/^[A-Za-z0-9]$/.test('Ab') //❌
Negating a pattern
188
Regular Expressions
/[^A-Za-z0-9]/.test('a') //❌
/[^A-Za-z0-9]/.test('1') //❌
/[^A-Za-z0-9]/.test('A') //❌
/[^A-Za-z0-9]/.test('@') //✅
Meta characters
\d matches any digit, equivalent to [0-9]
\s matches any whitespace character: spaces, tabs, newlines and Unicode spaces
\0 matches null
\uXXXX matches a unicode character with code XXXX (requires the u flag)
. matches any character that is not a newline char (e.g. \n ) (unless you use the s
/hey|ho/.test('hey') //✅
/hey|ho/.test('ho') //✅
Quantifiers
Say you have this regex, that checks if a string has one digit in it, and nothing else:
/^\d$/
You can use the ? quantifier to make it optional, thus requiring zero or one:
189
Regular Expressions
/^\d?$/
+
Match one or more (>=1) items
/^\d+$/
/^\d+$/.test('12') //✅
/^\d+$/.test('14') //✅
/^\d+$/.test('144343') //✅
/^\d+$/.test('') //❌
/^\d+$/.test('1a') //❌
*
Match 0 or more (>= 0) items
/^\d+$/
/^\d*$/.test('12') //✅
/^\d*$/.test('14') //✅
/^\d*$/.test('144343') //✅
/^\d*$/.test('') //✅
/^\d*$/.test('1a') //❌
{n}
Match exactly n items
/^\d{3}$/
/^\d{3}$/.test('123') //✅
/^\d{3}$/.test('12') //❌
/^\d{3}$/.test('1234') //❌
/^[A-Za-z0-9]{3}$/.test('Abc') //✅
{n,m}
Match between n and m times:
190
Regular Expressions
/^\d{3,5}$/
/^\d{3,5}$/.test('123') //✅
/^\d{3,5}$/.test('1234') //✅
/^\d{3,5}$/.test('12345') //✅
/^\d{3,5}$/.test('123456') //❌
/^\d{3,}$/
/^\d{3,}$/.test('12') //❌
/^\d{3,}$/.test('123') //✅
/^\d{3,}$/.test('12345') //✅
/^\d{3,}$/.test('123456789') //✅
Optional items
Following an item with ? makes it optional:
/^\d{3}\w?$/
/^\d{3}\w?$/.test('123') //✅
/^\d{3}\w?$/.test('123a') //✅
/^\d{3}\w?$/.test('123ab') //❌
Groups
Using parentheses, you can create groups of characters: (...)
This example matches exactly 3 digits followed by one or more alphanumeric characters:
/^(\d{3})(\w+)$/
/^(\d{3})(\w+)$/.test('123') //❌
/^(\d{3})(\w+)$/.test('123s') //✅
/^(\d{3})(\w+)$/.test('123something') //✅
/^(\d{3})(\w+)$/.test('1234') //✅
Repetition characters put after a group closing parentheses refer to the whole group:
/^(\d{2})+$/
/^(\d{2})+$/.test('12') //✅
/^(\d{2})+$/.test('123') //❌
191
Regular Expressions
/^(\d{2})+$/.test('1234') //✅
Capturing Groups
So far, we've seen how to test strings and check if they contain a certain pattern.
A very cool feature of regular expressions is the ability to capture parts of a string, and put
them into an array.
String.match(RegExp)
RegExp.exec(String)
They are exactly the same, and return an Array with the whole matched string in the first item,
then each matched group content.
'123s'.match(/^(\d{3})(\w+)$/)
//Array [ "123s", "123", "s" ]
/^(\d{3})(\w+)$/.exec('123s')
//Array [ "123s", "123", "s" ]
'hey'.match(/(hey|ho)/)
//Array [ "hey", "hey" ]
/(hey|ho)/.exec('hey')
//Array [ "hey", "hey" ]
/(hey|ho)/.exec('ha!')
//null
When a group is matched multiple times, only the last match is put in the result array:
'123456789'.match(/(\d)+/)
//Array [ "123456789", "9" ]
Optional groups
A capturing group can be made optional by using (...)? . If it's not found, the resulting array
slot will contain undefined :
192
Regular Expressions
A group can be assigned to a name, rather than just being assigned a slot in the result array:
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
const result = re.exec('2015-01-02')
/hey|ho/.exec('hey') // [ "hey" ]
193
Regular Expressions
Noncapturing Groups
Since by default groups are Capturing Groups, you need a way to ignore some groups in the
resulting array. This is possible using Noncapturing Groups, which start with an (?:...)
'123s'.match(/^(\d{3})(?:\s)(\w+)$/)
//null
'123 s'.match(/^(\d{3})(?:\s)(\w+)$/)
//Array [ "123 s", "123", "s" ]
Flags
You can use the following flags on any regular expression:
m : enables multiline mode. In this mode, ^ and $ match the start and end of the
whole string. Without this, with multiline strings they match the beginning and end of each
line.
u : enables support for unicode (introduced in ES6/ES2015)
s : (new in ES2018) short for single line, it causes the . to match new line characters
as well
Flags can be combined, and they are added at the end of the string in regex literals:
/hey/ig.test('HEy') //✅
Inspecting a regex
Given a regex, you can inspect its properties:
194
Regular Expressions
lastIndex
/^(\w{3})$/i.source //"^(\\d{3})(\\w+)$"
/^(\w{3})$/i.multiline //false
/^(\w{3})$/i.lastIndex //0
/^(\w{3})$/i.ignoreCase //true
/^(\w{3})$/i.global //false
Escaping
These characters are special:
[ ]
( )
{ }
They are special because they are control characters that have a meaning in the regular
expression pattern, so if you want to use them inside the pattern as matching characters, you
need to escape them, by prepending a backslash:
/^\\$/
/^\^$/ // /^\^$/.test('^') ✅
/^\$$/ // /^\$$/.test('$') ✅
String boundaries
\b and \B let you inspect whether a string is at the beginning or at the end of a word:
195
Regular Expressions
Example:
The String object in JavaScript has a replace() method, which can be used without regular
expressions to perform a single replacement on a string:
Using the g flag is the only way to replace multiple occurrences in a string in vanilla
JavaScript:
Groups let us do more fancy things, like moving around parts of a string:
Instead of using a string you can use a function, to do even fancier things. It will receive a
number of arguments like the one returned by String.match(RegExp) or RegExp.exec(String) ,
with a number of arguments that depends on the number of groups:
196
Regular Expressions
Greediness
Regular expressions are said to be greedy by default.
/\$(.+)\s?/
Why? Because the regex after the $ sign matches any character with .+ , and it won't stop
until it reaches the end of the string. Then, it finishes off because \s? makes the ending
space optional.
To fix this, we need to tell the regex to be lazy, and perform the least amount of matching
possible. We can do so using the ? symbol after the quantifier:
I removed the ? after \s otherwise it matched only the first number, since the space
was optional
So, ? means different things based on its position, because it can be both a quantifier and a
lazy mode indicator.
197
Regular Expressions
/Roger(?=Waters)/
?! performs the inverse operation, matching if a string is not followed by a specific substring:
/Roger(?!Waters)/
/(?<=Roger) Waters/
/(?<!Roger) Waters/
198
Regular Expressions
If you don't add that flag, this simple regex that should match one character will not work,
because for JavaScript that emoji is represented internally by 2 characters (see Unicode in
JavaScript):
/^.$/.test('a') //✅
/^.$/.test(' ') //❌
/^.$/u.test(' ') //✅
/[a-z]/.test('a') //✅
/[1-9]/.test('1') //✅
JavaScript checks the internal code representation, so < < because \u1F436 <
\u1F43A < \u1F98A . Check the full Emoji list to get those codes, and to find out the order (tip:
the macOS Emoji picker has some emojis in a mixed order, don't count on it)
Unicode property escapes is an ES2018 feature that introduces a very cool feature, extending
this concept to all Unicode characters introducing \p{} and is negation \P{} .
Any unicode character has a set of properties. For example Script determines the language
family, ASCII is a boolean that's true for ASCII characters, and so on. You can put this
property in the graph parentheses, and the regex will check for that to be true:
/^\p{ASCII}+$/u.test('abc') //✅
/^\p{ASCII}+$/u.test('ABC@') //✅
/^\p{ASCII}+$/u.test('ABC ') //❌
ASCII_Hex_Digit is another boolean property, that checks if the string only contains valid
hexadecimal digits:
199
Regular Expressions
/^\p{ASCII_Hex_Digit}+$/u.test('0123456789ABCDEF') //✅
/^\p{ASCII_Hex_Digit}+$/u.test('h') //❌
There are many other boolean properties, which you just check by adding their name in the
graph parentheses, including Uppercase , Lowercase , White_Space , Alphabetic , Emoji and
more:
/^\p{Lowercase}$/u.test('h') //✅
/^\p{Uppercase}$/u.test('H') //✅
/^\p{Emoji}+$/u.test('H') //❌
/^\p{Emoji}+$/u.test(' ') //✅
In addition to those binary properties, you can check any of the unicode character properties to
match a specific value. In this example, I check if the string is written in the greek or latin
alphabet:
/^\p{Script=Greek}+$/u.test('ελληνικά') //✅
/^\p{Script=Latin}+$/u.test('hey') //✅
Read more about all the properties you can use directly on the proposal.
Examples
Extract a number from a string
Supposing a string has only one number you need to extract, /\d+/ should do it:
'Test 123123329'.match(/\d+/)
// Array [ "123123329" ]
/(\S+)@(\S+)\.(\S+)/
/(\S+)@(\S+)\.(\S+)/.exec('copesc@gmail.com')
//["copesc@gmail.com", "copesc", "gmail", "com"]
This is a simplistic example however, as many invalid emails are still satisfied by this regex.
200
Regular Expressions
The best way to do so is by using a capturing group, because we know the match starts and
ends with " , and we can easily target it, but we also want to remove those quotes from our
result.
/<span\b[^>]*>(.*?)<\/span>/
/<span\b[^>]*>(.*?)<\/span>/.exec('test')
// null
/<span\b[^>]*>(.*?)<\/span>/.exec('<span>test</span>')
// ["<span>test</span>", "test"]
/<span\b[^>]*>(.*?)<\/span>/.exec('<span class="x">test</span>')
// ["<span class="x">test</span>", "test"]
201
Glossary
Glossary
A guide to a few terms used in frontend development that might be alien to
you
Asynchronous
Block
Block Scoping
Callback
Declarative
Fallback
Function Scoping
Immutability
Lexical Scoping
Polyfill
Pure function
Reassignment
Scope
Scoping
Shim
Side effect
State
Stateful
Stateless
Strict mode
Tree Shaking
Asynchronous
Code is asynchronous when you initiate something, forget about it, and when the result is
ready you get it back without having to wait for it. The typical example is an AJAX call, which
might take even seconds and in the meantime you complete other stuff, and when the
response is ready, the callback function gets called. Promises and async/await are the modern
way to handle async.
Block
202
Glossary
Block Scoping
With Function Scoping, any variable defined in a block is visible and accessible from inside the
whole block, but not outside of it.
Callback
A callback is a function that's invoked when something happens. A click event associated to
an element has a callback function that's invoked when the user clicks the element. A fetch
request has a callback that's called when the resource is downloaded.
Declarative
A declarative approach is when you tell the machine what you need to do, and you let it figure
out the details. React is considered declarative, as you reason about abstractions rather than
editing the DOM directly. Every high level programming language is more declarative than a
low level programming language like Assembler. JavaScript is more declarative than C. HTML
is declarative.
Fallback
A fallback is used to provide a good experience when a user hasn't access to a particular
functionality. For example a user that browses with JavaScript disabled should be able to have
a fallback to a plain HTML version of the page. Or for a browser that has not implemented an
API, you should have a fallback to avoid completely breaking the experience of the user.
Function Scoping
With Function Scoping, any variable defined in a function is visible and accessible from inside
the whole function.
Immutability
203
Glossary
A variable is immutable when its value cannot change after it's created. A mutable variable
can be changed. The same applies to objects and arrays.
Lexical Scoping
Lexical Scoping is a particular kind of scoping where variables of a parent function are made
available to inner functions as well. The scope of an inner function also includes the scope of a
parent function.
Polyfill
A polyfill is a way to provide new functionality available in modern JavaScript or a modern
browser API to older browsers. A polyfill is a particular kind of shim.
Pure function
A function that has no side effects (does not modify external resources), and its output is only
determined by the arguments. You could call this function 1M times, and given the same set of
arguments, the output will always be the same.
Reassignment
JavaScript with var and let declaration allows you to reassign a variable indefinitely. With
const declarations you effectively declare an immutable value for strings, integers, booleans,
and an object that cannot be reassigned (but you can still modify it through its methods).
Scope
Scope is the set of variables that's visible to a part of the program.
Scoping
Scoping is the set of rules that's defined in a programming language to determine the value of
a variable.
Shim
204
Glossary
A shim is a little wrapper around a functionality, or API. It's generally used to abstract
something, pre-fill parameters or add a polyfill for browsers that do not support some
functionality. You can consider it like a compatibility layer.
Side effect
A side effect is when a function interacts with some other function or object outside it.
Interaction with the network or the file system, or with the UI, are all side effects.
State
State usually comes into play when talking about Components. A component can be stateful if
it manages its own data, or stateless if it doesn't.
Stateful
A stateful component, function or class manages its own state (data). It could store an array, a
counter or anything else.
Stateless
A stateless component, function or class is also called dumb because it's incapable of having
its own data to make decisions, so its output or presentation is entirely based on its
arguments. This implies that pure functions are stateless.
Strict mode
Strict mode is an ECMAScript 5.1 new feature, which causes the JavaScript runtime to catch
more errors, but it helps you improve the JavaScript code by denying undeclared variables
and other things that might cause overlooked issues like duplicated object properties and other
subtle things. Hint: use it. The alternative is "sloppy mode" which is not a good thing even
looking at the name we gave it.
Tree Shaking
205
Glossary
Tree shaking means removing "dead code" from the bundle you ship to your users. If you add
some code that you never use in your import statements, that's not going to be sent to the
users of your app, to reduce file size and loading time.
206