JavaScript PDF
JavaScript PDF
of Contents
1. Preface
2. Introduction to JavaScript
3. ECMAScript
4. ES6
5. ES2016
6. ES2017
7. ES2018
8. Coding style
9. Lexical Structure
10. Variables
11. Types
12. Expressions
13. Prototypal inheritance
14. Classes
15. Exceptions
16. Semicolons
17. Quotes
18. Template Literals
19. Functions
20. Arrow Functions
21. Closures
22. Arrays
23. Loops
24. Events
25. The Event Loop
26. Asynchronous programming and callbacks
27. Promises
28. Async and Await
29. Loops and Scope
30. Timers
31. this
32. Strict Mode
33. Immediately-invoked Function Expressions (IIFE)
34. Math operators
35. The Math object
36. ES Modules
37. CommonJS
38. Glossary
Preface
The JavaScript Handbook follows the 80/20 rule: learn in 20% of the time the
80% of a topic.
I find this approach gives a well-rounded overview. This book does not try to
cover everything under the sun related to JavaScript. If you think some specific
topic should be included, tell me.
You can reach me on Twitter @flaviocopes.
I hope the contents of this book will help you achieve what you want: learn the
basics of JavaScript.
This book is written by Flavio. I publish web development tutorials every day
on my website flaviocopes.com.
Enjoy!
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, PHP, and more traditional server-side
languages. Learn all about it!
Introduction
JavaScript is one of the most popular programming languages in the world.
Created 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 stuck.
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 ecosystems 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.
A basic definition of JavaScript
JavaScript is a programming language that is:
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 canceled due to feature creep (they were trying to
add too many things at once), while ES5 was a huge version for JS.
ES2015, also called ES6, was huge as well.
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.
Currently, the latest approved JavaScript version is ES2017.
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
Whenever you read about JavaScript you'll inevitably see one of these terms:
ES3
ES5
ES6
ES7
ES8
ES2015
ES2016
ES2017
ECMAScript 2017
ECMAScript 2016
ECMAScript 2015
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.
Current ECMAScript version
The current ECMAScript version is ES2018.
It was released in June 2018.
When is the next version coming out?
Historically JavaScript editions have been standardized during the summer, so
we can expect ECMAScript 2019 to be released in summer 2019, but this is just
speculation.
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.
This table should clear things a bit:
to
This is not a breaking change, regular function s will continue to work just as
before.
A new this scope
The this scope with arrow functions is inherited from the context.
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).
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.
By using promises you can rewrite this code
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.
Generators enable whole new paradigms of programming in JavaScript,
allowing:
2-way communication while a generator is running
long-lived while loops which do not freeze your program
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 function is just paused).
In the second iteration we pass the value 7 :
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 .
In the next, and last, iteration, we pass in 100
calc.next(100)
and in return we got
{
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 .
let and const
var is traditionally function scoped.
let is a new variable declaration which is block scoped.
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.
const is just like let , but immutable.
In JavaScript moving forward, you'll see little to no var declarations any more,
just let and const .
in particular, maybe surprisingly, is very widely used nowadays with
const
immutability being very popular.
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 + '.'
}
}
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
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:
string
is awesome!`
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:
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
Enhanced Object Literals
In ES2015 Object Literals gained superpowers.
you can do
Prototype
A prototype can be specified with
super()
const anObject = { y: 'y', test: () => 'zoo' }
const x = {
__proto__: anObject,
test() {
return super.test() + 'x'
}
}
x.test() //zoox
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:
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.
Math.pow(4, 2) == 4 ** 2
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'
padEnd()
'test'.padEnd(4) 'test'
'test'.padEnd(5) 'test_'
'test'.padEnd(8) 'test____'
'test'.padEnd(8, 'abcd') 'testabcd'
(in the table, _ = space)
Object.values()
This method returns an array containing all the object own property values.
Usage:
const person2 = {}
Object.assign(person2, person1)
const person3 = {}
Object.defineProperties(person3, Object.getOwnPropertyDescriptors(person1))
person1.name = 'x'
;('x')
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.
A quick example
Code making use of asynchronous functions can be written as
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)
})
Shared Memory and Atomics
WebWorkers are used to create multithreaded programs in the browser.
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.
ES2018
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 ES2018, aka ES9
ES2018 is the latest version of the ECMAScript standard.
What are the new things introduced in it?
Rest/Spread Properties
ES6 introduced the concept of a rest element when working with array
destructuring:
first // 1
second // 2
others // { third: 3, fourth: 4, fifth: 5 }
Since this uses await , you can use it only inside async functions, like a normal
await (see async/await)
Promise.prototype.finally()
When a promise is fulfilled, successfully it calls the then() methods, one after
another.
If something fails during this, the then() methods are jumped and the catch()
method is executed.
allow you to run some code regardless of the successful or not
finally()
successful execution of the promise:
fetch('file.json')
.then(data => data.json())
.catch(error => console.error(error))
.finally(() => console.log('finished'))
Regular Expression improvements
RegExp lookbehind assertions: match a string depending
on what precedes it
This is a lookahead: you use ?= to match a string that's followed by a specific
substring:
/Roger(?=Waters)/
/Roger(?!Waters)/
/(?<=Roger) Waters/
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 ') //❌
/^\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.
const re = /(?<year>\d{4})-(?<month>\d{2})-(?<day>\d{2})/
const result = re.exec('2015-01-02')
/hi.welcome/.test('hi\nwelcome') // false
/hi.welcome/s.test('hi\nwelcome') // true
Coding style
This JavaScript Coding Style is the set of conventions I
use every day when using JavaScript. It's a live document,
with the main set of rules I follow
A coding style is an agreement with yourself and your team, to keep
consistency on a project.
An if you don't have a team, it's an agreement with you, to always keep your
code up to your standards.
Having fixed rules on your code writing format helps a lot in order to have a
more readable and managed code.
Popular Style Guides
There are a quite a few of them around, here are the 2 most common ones in the
JavaScript world:
The Google JavaScript Style Guide
The AirBnb JavaScript Style Guide
It's up to you to follow one of those, or create your own style guide.
Be consistent with the project you work
on
Even if you prefer a set of styles, when working on a project you should use that
project style.
An Open Source project on GitHub might follow a set of rules, another project
you work on with a team might follow an entirely different one.
Prettier is an awesome tool that enforces code formatting, use it.
My own preferences
My own take on JavaScript style is:
Always use the latest ES version. Use Babel if old browser support is necessary.
Indentation: use spaces instead of tabs, indent using 2 spaces.
Semicolons: don't use semicolons.
Line length: try to cut lines at 80 chars, if possible.
Inline Comments: use inline comments in your code. Use block comments only
to document.
No dead code: Don't leave old code commented, "just in case" it will be useful
later. Keep only the code you need now, version control/your notes app is meant
for this.
Only comment when useful: Don't add comments that don't help understand
what the code is doing. If the code is self-explaining through the use of good
variable and function naming, and JSDoc function comments, don't add a
comment.
Variable declarations: always declare variables to avoid polluting the global
object. Never use var . Default to const , only use let if you reassign the
variable.
Constants: declare all constants in CAPS. Use _ to separate words in a
VARIABLE_NAME .
Functions: use arrow functions unless you have a specific reason to use regular
functions, like in object methods or constructors, due to how this works.
Declare them as const, and use implicit returns if possible.
Feel free to use nested functions to hide helper functions to the rest of the code.
Names: function names, variable names and method names always start with a
lowercase letter (unless you identify them as private, read below), and are
camelCased. Only constructor functions and class names should start capitalized.
If you use a framework that requires specific conventions, change your habits
accordingly. File names should all be lowercase, with words separated by - .
Statement-specific formats and rules:
if
if (condition) {
statements
}
if (condition) {
statements
} else {
statements
}
if (condition) {
statements
} else if (condition) {
statements
} else {
statements
}
for
Always initialize the length in the initialization to cache it, don't insert it in the
condition.
Avoid using for in except with used in conjunction with .hasOwnProperty() .
Prefer for of (see JavaScript Loops)
while
while (condition) {
statements
}
do
do {
statements
} while (condition);
switch
switch (expression) {
case expression:
statements
default:
statements
}
try
try {
statements
} catch (variable) {
statements
}
try {
statements
} catch (variable) {
statements
} finally {
statements
}
return
variable
/* */
//
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.
Literals and Identifiers
We define as literal a value that is written in the source code, for example a
number, a string, a boolean or also more advanced constructs, like Object
Literals or Array Literals:
5
'Test'
true
['a', 'b']
{color: 'red', shape: 'Rectangle'}
Test
test
TEST
_test
Test1
$test
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
protected
static
yield
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.
var a = 1
var a = 2
You can also declare multiple variables at once in the same statement:
var a = 1, b = 2
const a = 'test'
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.
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
Primitive types are
Numbers
Strings
Booleans
10
5354576767321
0xCC //hex
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:
`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.
Falsy values, values interpreted as false, are
0
-0
NaN
undefined
null
'' //empty string
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.
To detect if a value is undefined , you use the construct:
1 / 2
i++
i -= 2
i * 2
String expressions
Expressions that evaluate to a string:
2
0.02
'something'
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
Array and object initializers expressions
[] //array literal
{} //object literal
[1,2,3]
{a: 1, b: 2}
{a: {b: 1}}
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
Property access expressions
object.property //reference a property (or method) of an object
object[property]
object['property']
Object creation expressions
new object()
new a(1)
new MyRectangle('name', 2, {a: 4})
Function definition expressions
function() {}
function(a, b) { return a * b }
(a, b) => a * b
a => a * 2
() => { return 2 }
Invocation expressions
The syntax for calling a function or method
a.x(2)
window.resize()
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.
While most object-oriented languages use a class-based inheritance model,
JavaScript is based on the prototype inheritance model.
What does this mean?
Every single JavaScript object has a property, called prototype , which points to a
different object.
This different object is the object prototype.
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()
the prototype is Array .
You can verify this by checking the __proto__ getter:
All the properties and methods of the prototype are available to the object that
has that prototype:
You can check the prototype of an object using the isPrototypeOf() method:
Array.isPrototypeOf(list) //true
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:
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.
There are workarounds, but I won't describe them here.
Getters and setters
You can add methods prefixed with get or set to create a getter and setter,
which are two different pieces of code that are execute based on what you are
doing: accessing the variable, or modifying its value.
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
}
}
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
try {
//lines of code
} catch (e) {
You can add multiple handlers, that can catch different kinds of errors.
finally
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 {
}
Nested try blocks
blocks can be nested, and an exception is always handled in the nearest
try
catch block:
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.
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.
The process that does this is called Automatic Semicolon Insertion.
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.
The rules of JavaScript Automatic
Semicolon Insertion
The JavaScript parser will automatically add a semicolon when, during the
parsing of the source code, it finds these particular situations:
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
Examples of code that does not do what
you think
Based on those rules, here are some examples.
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'
}
})()
(() => {
return {
color: 'white'
}
})()
1 + 1
-1 + 1 === 0 ? alert(0) : alert(2)
And ultimately, always test your code to make sure it does what you want
Quotes
An overview of the quotes allowed in JavaScript and their
unique features
JavaScript allows you to use 3 types of quotes:
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.
They have a unique feature: they allow multiline strings.
Multiline strings are also possible using regular strings, using escape characters:
const multilineString = 'A string\non multiple lines'
Not just that. You can interpolate variables using the ${} syntax:
They are unique because they provide a lot of features that normal strings built
with quotes, in particular:
they offer a great syntax to define multiline strings
they provide an easy way to interpolate variables and expressions in strings
they allow to create DSLs with template tags
This allows to create a string on 2 lines, but it's rendered on just one line:
first part second part
To render the string on multiple lines as well, you explicitly need to add \n at
the end of each line, like this:
or
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
Template literals provide an easy way to interpolate variables and expressions
into strings.
You do so by using the ${...} syntax:
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.
is an array containing the template literal content tokenized by the
literals
expressions interpolations.
expressions contains all the interpolations.
If we take an example above:
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.
expressions in this case is an array with a single item, 6 .
A more complex example is:
`something
another `
`
new line `
`
test`
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.
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:
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:
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()
this in Arrow Functions
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}`)
}
}
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).
IIFE, Immediately Invocated Function
Expressions
An IIFE is a function that's immediately executed right after its declaration:
;(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:
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')
}
Not going to work:
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:
This is because var declarations are hoisted and initialized with undefined as a
value, while const and let are hoisted but not initialized.
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:
Thanks to this short syntax, arrow functions encourage the use of small
functions.
Implicit return
Arrow functions allow you to have an implicit return: values are returned
without having to use the return keyword.
It works when there is a on-line statement in the function body:
myFunction() //'test'
const car = {
model: 'Fiesta',
manufacturer: 'Ford',
fullName: function() {
return `${this.manufacturer} ${this.model}`
}
}
const car = {
model: 'Fiesta',
manufacturer: 'Ford',
fullName: () => {
return `${this.manufacturer} ${this.model}`
}
}
bark(`Roger`)
bark()
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.
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
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)
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:
// return value is 24
forEach
ES6
a.forEach(f)
a.forEach(v => {
console.log(v)
})
for..of
ES6
for (let v of a) {
console.log(v)
}
for
@@iterator
ES6
Getting the iterator from an array returns an iterator of values
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
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
a.push(4)
a.unshift(0)
a.unshift(-2, -1)
Removing an item from an array
From the end
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()
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.
A commonly used syntax is:
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 :
a.findIndex((element, index, array) => {
//return true or false
})
ES7
a.includes(value)
a.includes(value, i)
a.reverse()
Get a string representation of an array
a.toString()
a.join()
a.join(', ')
Copy an existing array by value
const b = Array.from(a)
const b = Array.of(...a)
Copy just some values from an existing
array
const b = Array.from(a, x => x % 2 == 0)
Copy portions of an array into the array
itself, in other positions
const a = [1, 2, 3, 4]
a.copyWithin(0, 2) // [3, 4, 3, 4]
const b = [1, 2, 3, 4, 5]
b.copyWithin(0, 2) // [3, 4, 5, 4, 5]
//0 is where to start copying into,
// 2 is where to start copying from
const c = [1, 2, 3, 4, 5]
c.copyWithin(0, 2, 4) // [3, 4, 3, 4, 5]
//4 is an end index
Loops
JavaScript provides many way to iterate through loops.
This tutorial explains all the various loop possibilities in
modern JavaScript
Introduction
JavaScript provides many way to iterate through loops. This tutorial explains
each one with a small example and the main properties.
for
//index is optional
list.forEach(item => console.log(item))
do {
if (something) break
} while (true)
do {
if (something) continue
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.
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
The difference with for...in is:
for...of iterates over the property values
for...in iterates the property names
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
JavaScript in the browser uses an event-driven programming model.
Everything starts by following an event.
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.
There are a lot of different kind of events.
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.
JavaScript offer three ways to register an event handler:
window.onload = () => {
//window loaded
}
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.
Listening on different elements
You can listen on window to intercept "global" events, like the usage of the
keyboard, and you can listen on specific elements to check events happening on
them, like a mouse click on a button.
This is why addEventListener is sometimes called on window , sometimes on a
DOM element.
The Event object
An event handler gets an Event object as the first parameter:
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 to its parents, unless you stop the propagation (see later).
Those event listeners will be called in order, and this order is determined by the
event bubbling/capturing model used.
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 .
By default all events bubble.
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
)
Note that first all capturing event handlers are run.
Then all the bubbling event handlers.
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).
Stopping the propagation
An event on a DOM element will be propagated to all its parent elements tree,
unless it's stopped.
<html>
<body>
<section>
<a id="my-link" ...>
event.stopPropagation()
})
Popular events
Here's a list of the most common events you will likely handle.
Load
is fired on window and the body element when the page has finished
load
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 throttling later)
Keyboard events
fires when a keyboard button is pressed (and any time the key repeats
keydown
while the button stays pressed). keyup is fired when the key is released.
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 throttling instead.
Throttling
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 throttling 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:
foo()
foo
bar
baz
as expected.
When this code runs, first foo() is called. Inside foo() we first call bar() , then
we call baz() .
At this point the call stack looks like this:
The event loop on every iteration looks if there's something in the call stack, and
executes it:
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().
At this point the call stack looks like this:
Here is the execution order for all the functions in our program:
Why is this happening?
The Message Queue
When setTimeout() is called, the Browser or Node.js start the timer. Once the
timer expires, in this case immediately as we put 0 as the timeout, the callback
function is put in the Message Queue.
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.
ES6 Job Queue
ECMAScript 2015 introduced the concept of the Job Queue, which is used by
Promises (also introduced in ES6/ES2015). It's a way to execute the result of an
async function as soon as possible, rather than being put at the end of the call
stack.
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 the 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
baz
should be right after baz, before bar
bar
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
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
})
window.addEventListener('load', () => {
//window loaded
//do what you want
})
setTimeout(() => {
// runs after 2 seconds
}, 2000)
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.
How do we solve this?
Alternatives to callbacks
Starting with ES6, JavaScript introduced several features that help us with
asynchronous code that do not involve using callbacks:
Promises (ES6)
Async/Await (ES8)
Promises
Promises are one way to deal with asynchronous code in
JavaScript, without writing too many callbacks in your
code.
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.
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
In the last section, we introduced how a promise is created.
Now let's see how the promise can be consumed or used.
Running checkIfItsDone() will execute the isItDoneYet() promise and will wait
for it to resolve, using the then callback, and if there is an 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)
.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:
status , a numeric value representing the HTTP status code
statusText , a status message, which is OK if the request succeeded
also has a json() method, which returns a promise that will resolve
response
with the content of the body processed and transformed into JSON.
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)
})
// or
Cascading errors
If inside the catch() you raise an error, you can append a second catch() to
handle it, and so on.
const f1 = fetch('/something.json')
const f2 = fetch('/something2.json')
You are not limited to using fetch of course, any promise is good to go.
Promise.race()
runs when the first of the promises you pass to it resolves, and it
Promise.race()
runs the attached callback just once, with the result of the first promise resolved.
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:
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
Promise all the things
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.
This is why this code is valid:
getFirstUserData()
getFirstUserData()
Multiple async functions in series
Async functions can be chained very easily, and the syntax is much more
readable than with plain promises:
watchOverSomeoneWatchingSomeoneDoingSomething().then((res) => {
console.log(res)
})
Will print:
const operations = []
0
1
2
3
4
5
5
5
5
5
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.
So how should we do to make things work as we want?
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.
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 = []
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:
setTimeout returns the timer id. This is generally not used, but you can store this
id, and clear it if you want to delete this scheduled function execution:
// 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)
setInterval(() => {
// runs every 2 seconds
}, 2000)
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 :
setTimeout(myFunction, 1000)
}
setTimeout(
myFunction()
}, 1000)
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 in strict mode
Outside any object, this in strict mode is always undefined .
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.
Which means window in a browser context.
this in methods
A method is a function attached to an object.
You can see it in various forms.
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!`)
}
}
car.drive()
//Driving a undefined undefined car!
Binding arrow functions
You cannot bind a value to an arrow function, like you do with normal functions.
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.
Explicitly pass an object to be used as
this
JavaScript offers a few ways to map this to any object you want.
Using bind() , at the function declaration step:
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!
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.
The special case of browser event
handlers
In event handlers callbacks, this refers to the HTML element that received the
event:
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)
)
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 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)
How to enable 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.
What changes in Strict Mode
;(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:
;(function() {
'use strict'
variable = 'hey'
})()(() => {
'use strict'
myname = 'Flavio'
})()
Assignment errors
JavaScript silently fails some conversion errors.
In Strict Mode, those silent errors now raise issues:
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:
The same works for getters:
const car = {
get color() {
return 'blue'
}
}
car.color = 'red'(
//ok
() => {
'use strict'
car.color = 'yellow' //TypeError: Cannot set property color of #
<Object> which has only 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(
//false
() => {
'use strict'
delete Object.prototype //TypeError: Cannot delete property 'prototype' of function Object() {
}
)()
(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.
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.
Immediately-invoked Function
Expressions (IIFE)
An Immediately-invoked Function Expression is a way to
execute functions immediately, as soon as they are created.
IIFEs are very useful because they don't pollute the global
object, and they are a simple way to isolate variables
declarations
An Immediately-invoked Function Expression (IIFE for friends) is a way to
execute functions immediately, as soon as they are created.
IIFEs are very useful because they don't pollute the global object, and they are
a simple way to isolate variables declarations.
This is the syntax that defines an IIFE:
;(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() {
/* */
}())
(() => {
/* */
}())
Alternative syntax using unary operators
There is some weirder syntax that you can use to create an IIFE, but it's very
rarely used in the real world, and it relies on using any unary operator:
;-(function() {
/* */
})() +
(function() {
/* */
})()
~(function() {
/* */
})()
!(function() {
/* */
})()
;(function doSomething() {
/* */
})()
IIFEs starting with a semicolon
You might see this in the wild:
;(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.
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.
JavaScript offers several operators to help us work with numbers.
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 (https://flaviocopes.com/)
Returns the quotient of the first operator and the second:
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
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
Unary plus (+)
If the operand is not a number, it tries to convert it. Otherwise if the operand is
already a number, it does nothing.
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
/= : division assignment
%= : remainder assignment
**= : exponentiation 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
Following this table, we can solve this calculation:
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
The Math object
The Math object contains lots of utilities math-related.
This tutorial describes them all
The Math object contains lots of utilities math-related.
It contains constants and functions.
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
The operand must be between -1 and 1
Math.acos(0.8) //0.6435011087932843
Math.asin()
Returns the arcsine of the operand
The operand must be between -1 and 1
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
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
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
ES Modules
ES Modules is the ECMAScript standard for working
with modules. While Node.js has been using the
CommonJS standard since years, the browser never had a
module system, as every major decision such as a module
system must be first standardized by ECMAScript and
then implemented
Introduction to ES Modules
ES Modules is the ECMAScript standard for working with modules.
While Node.js has been using the CommonJS standard since years, the browser
never had a module system, as every major decision such as a module system
must be first standardized by ECMAScript and then implemented by the
browser.
This standardization process completed with ES6 and browsers started
implementing this standard trying to keep everything well aligned, working all in
the same way, and now ES Modules are supported in Chrome, Safari, Edge and
Firefox (since version 60).
Modules are very cool, because they let you encapsulate all sorts of
functionality, and expose this functionality to other JavaScript files, as libraries.
The ES Modules Syntax
The syntax to import a module is:
A module is a JavaScript file that exports one or more value (objects, functions
or variables), using the export keyword. For example, this module exports a
function that returns a string uppercase:
uppercase.js
Note: this module import behaves like a defer script load. See efficiently
load JavaScript with defer and async
It's important to note that any script loaded with type="module" is loaded in strict
mode.
In this example, the uppercase.js module defines a default export, so when we
import it, we can assign it a name we prefer:
toUpperCase('test') //'TEST'
You can also use an absolute path for the module import, to reference modules
defined on another domain:
This is not:
This creates one default export. In a file however you can export more than one
thing, by using this syntax:
const a = 1
const b = 2
const c = 3
export { a, b, c }
You can import just a few of those exports, using the destructuring assignment:
You can import the default export, and any non-default export by name, like in
this common React import:
const { a, b, c } = require('./uppercase.js')
//file.js
module.exports = value