[flaviocopes] The JavaScript Fundamentals Course - Language basics

Learn the JavaScript Fundamentals to the roots, using this hands-on course.

Introduction to JavaScript

JavaScript is one of the most popular programming languages in the world.

I believe it’s a great language to be your first programming language ever.

We mainly use JavaScript to create

  • websites
  • web applications
  • server-side applications using Node.js

but JavaScript is not limited to these things, and it can also be used to

  • create mobile applications using tools like React Native
  • create programs for microcontrollers and the internet of things
  • create smartwatch applications

It can basically do anything. It’s so popular that everything new that shows up is going to have some kind of JavaScript integration at some point.

The things you’ll learn in this course can be applied to all the things I mentioned.

A bit of history

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 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 Dynamic HTML .

With the growing needs that the web platform demanded (and continues to demand), JavaScript had the responsibility to grow as well, to accommodate the needs of one of the most widely used ecosystems of the world.

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, 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 like other languages like C would need, 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. Don’t worry if those things are unknown to you - you’ll know all of those at the end of the course.
  • 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 - which builds on top of JavaScript - provides)
  • 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 a 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 is loosely typed

In programming, we call a language loosely typed when you don’t have to explicitly specify types of variables and objects.

A strongly typed language, on the contrary, wants types specified.

There are pros and cons, you can argue forever but the reality is that both approaches are great, in their intended context and usage.

JavaScript is loosely typed. You don’t have to tell that a string is a string, nor you can require a function to accepts an integer as its parameter.

This gives JavaScript a lot of flexibility. Flexibility lets you move faster, change things quickly, iterate at a faster velocity.

A strong type system instead gives much more structure to a program and it’s a great aid for example, when working in teams when one single programmer can’t really have all the codebase in mind when working on it, and having types helps keep the code manageable.

This is typical of compiled languages (while famous dynamic languages like JavaScript, Python, and Ruby are loosely typed).

You trade some of the flexibility that a loosely typed language gives you to get more security and trust in the codebase.

The compiler thanks to types can detect errors at compile time, rather than at runtime, making it simpler to write code that does what you want (and makes the testing phase slightly easier, although nothing can make your programs perfect).

TypeScript is a great example of a strongly typed language. It compiles to JavaScript, giving you the benefit of the JavaScript platform plus the intended advantages of types.

Being loosely typed doesn’t mean you don’t have types, of course. You just make use of types implicitly, as you’ll soon see.

Lexical structure

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.

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 .

For example, I like to always 2 characters to indent.

Case sensitive

JavaScript is case sensitive. A variable named something is different from Something .

The same goes for any identifier.

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'}

An identifier is a sequence of characters that can be used to identify a variable, a function, an object. It can start with a letter, the dollar sign $ or an underscore _ , and it can contain digits. Using Unicode, a letter can be any allowed char, for example, an emoji :smile:.

Test
test
TEST
_test
Test1
$test

The dollar sign is commonly used to reference DOM elements.

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
protected
static
yield

because they are used, or reserved for future use, by the language.

Unicode

JavaScript is written in Unicode. This means you can use Emojis as variable names, but more importantly, you can write identifiers (variable, function and object names) in any language character set, for example, Japanese or Chinese. We’ll see more on Unicode later.

Values

A hello string is a value . A number like 12 is a value .

hello and 12 are values. string and number are the types of those values.

The type is the kind of value, its category. We have many different types in JavaScript, and we’ll talk about them in detail later on. Each type has its own characteristics.

When we need to have a reference to a value, we assign it to a variable . The variable can have a name, and the value is what’s stored in a variable, so we can later access that value through the variable name.

Variables

A variable is a value assigned to an identifier, so you can reference and use it later in the program.

Variables in JavaScript do not have any type attached. They are untyped .

Once you assign a value with some type to a variable, you can later reassign the variable to host a value of any other type, without any issue.

This is because JavaScript is loosely typed .

A variable must be declared before you can use it. There are 3 ways to do this, 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 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 //typeof a === 'undefined'

You can redeclare the variable many times, overriding it:

var a = 1
var a = 2

You can also declare multiple variables at once in the same statement:

var a = 1, b = 2

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 contained inner blocks. We’ll talk more about scope later.

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 it all makes much more sense

Using const

Variables declared with var or let can be changed later on in the program, and reassigned. 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.

const has block scope, same as let .

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.

This is an important concept. We should make use of any useful limitation that prevents us to do mistakes down the road.

We’ll later talk about a very important difference between those 3 variable declarations, in regards to scope and hoisting.

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
  • object types

Primitive types

Primitive types are

  • numbers
  • strings
  • booleans
  • symbol

And two special types, which only hold one specific value:

  • null
  • undefined

Primitive types store one value.

Object types

Any value that’s not of a primitive type (a string, a number, a boolean, null or undefined) is an object .

Object types have properties and also have methods that can act on those properties.

Lots of things at stake here. We’ll soon analyze everything related to in deep details.

Expressions

An expression is a single unit of JavaScript code that the JavaScript engine can evaluate, and return a value.

Expressions can vary in complexity. We start from the very simple ones, called primary expressions, to more complex ones. Each different expression produces a value.

In this lesson, I introduce you to the different expression types you can find.

Primary expressions

Under this category go variable references, literals, and constants:

2
0.02
'something'
true
false
this //the current scope
undefined
i //where i is a variable or a constant

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:

'A ' + 'string'

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

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()

Operators

Operators allow you to get two simple expressions and combine them to form a more complex expression.

We can classify operators based on the operands they work with. An operand is a simple expression. Some operators work with 1 operand. Most with 2 operands. Just one operator works with 3 operands.

The operator that works with 3 operands is the ternary operator, a succint way to express conditionals:

<condition> ? <expression> : <expression>

The <condition> is evaluated as a boolean, and upon the result, the operator runs the first expression (if the condition is true) or the second.

Example usage:

const running = true
(running === true) ? stop() : run()

We have the only 3-operands operator out of the way, so we can divide the rest of them in 2 parts: unary operators , and binary operators . After that, I will introduce the operator precedence concept.

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

Unary negation (-)

Return the negation of the operand

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

Logical not (!)

Invert the value of a boolean:

let value = true
!value //false

new

The new operator is used to create a new object. You follow new with the object class to create a new object of that type:

const date = new Date()

delete

delete is an operator which is not using a punctuation character, but is still an operator. It deletes a property from an object.

const car = {
  model: 'Fiesta',
  color: 'green'
}

delete car.model
delete car['color']

typeof

typeof returns a string representing the type of a variable:

typeof 'Flavio' //string
typeof 22 //number

await

await can only be used inside async functions, and it’s a way to signal JavaScript to wait for a promise to resolve. Don’t worry, we’ll talk about this extensively later.

const result = await something()

Spread operator

You can expand an array, an object or a string using the spread operator ... .

Let’s start with an array example. Given

const a = [1, 2, 3]

you can create a new array using

const b = [...a, 4, 5, 6]

You can also create a copy of an array using

const c = [...a]

This works for objects as well. Clone an object with:

const newObj = { ...oldObj }

Using strings, the spread operator creates an array with each char in the string:

const hey = 'hey'
const arrayized = [...hey] // ['h', 'e', 'y']

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:

const f = (foo, bar) => {}
const a = [1, 2]
f(...a)

The rest element is useful when working with array destructuring :

const numbers = [1, 2, 3, 4, 5]
[first, second, ...others] = numbers

and spread elements :

const numbers = [1, 2, 3, 4, 5]
const sum = (a, b, c, d, e) => a + b + c + d + e
const result = sum(...numbers)

ES2018 introduces rest properties, which are the same but for objects.

Rest properties :

const { first, second, ...others } = {
  first: 1,
  second: 2,
  third: 3,
  fourth: 4,
  fifth: 5
}

first // 1
second // 2
others // { third: 3, fourth: 4, fifth: 5 }

Spread properties allow to create a new object by combining the properties of the object passed after the spread operator:

const items = { first, second, ...others }
items //{ first: 1, second: 2, third: 3, fourth: 4, fifth: 5 }

It is also the perfect way to merge two simple objects into one:

const object1 = {
  name: 'Flavio'
}

const object2 = {
  age: 35
}

const object3 = {...object1, ...object2 }

Binary 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:

const result = 20 / 5 //result === 4
const result = 20 / 7 //result === 2.857142857142857

If you divide by zero, JavaScript does not raise any error but returns the Infinity value (or -Infinity if the value is negative).

1 / 0 //Infinity
-1 / 0 //-Infinity

Remainder (%)

The remainder is a very useful calculation in many use cases:

const result = 20 % 5 //result === 0
const result = 20 % 7 //result === 6

A remainder by zero is always NaN , a special value that means “Not a Number”:

1 % 0 //NaN
-1 % 0 //NaN

Multiplication (*)

Multiply two numbers

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

The exponentiation operator ** is the equivalent of using Math.pow() , but brought into the language instead of being a library function.

Math.pow(4, 2) == 4 ** 2

This feature is a nice addition for math intensive JS applications.

The ** operator is standardized across many languages including Python, Ruby, MATLAB, Lua, Perl and many others.

Assignment

Use = to assign a value to a variable:

const a = 2
let b = 2
var c = 2

Comparisons

You can use the following operators to compare two numbers, or two strings. Returns a boolean:

  • < less than
  • <= minus than, or equal to
  • > greater than
  • >= greater than, or equal to
const a = 2
a >= 1 //true

When comparing strings, it checks for the letter ordering, encoded in Unicode.

Equality checks

Accepts two values, and returns a boolean:

  • == checks for equality
  • != checks for inequality
  • === checks for strict equality
  • !== checks for strict inequality

Let’s talk what we mean for strict . Without the strict check, the second operand is converted to the type of the first before making the comparison. Strict prevents this.

Examples:

const a = true

a == true //true
a === true //true

1 == 1 //true
1 == '1' //true
1 === 1 //true
1 === '1' //false

You cannot check objects for equality: two objects are never equal to each other. The only case when a check might be true is if two variables reference the same object.

Some peculiarities to be aware: NaN is always different from NaN .

NaN == NaN //false

null and undefined values are equal if compared in non-strict mode:

null == undefined //true
null === undefined //false

Logical and

Returns true if both operands are true:

<expression> && <expression>

For example:

a === true && b > 3

The cool thing about this operator is that the second expression is never executed if the first evaluates to false. Which has some practical applications, for example, to check if an object is defined before using it:

const car = { color: 'green' }
const color = car && car.color

Logical or

Returns true if at least one of the operands is true:

<expression> || <expression>

For example:

a === true || b > 3

This operator is very useful to fallback to a default value. For example:

const car = {}
const color = car.color || 'green'

makes color default to green if car.color is not defined.

instanceof

Return true if the first operand is an instance of the object passed on the right, or one of its ancestors in its prototype chain:

class Car {}
class Fiesta extends Car {}

const myCar = new Fiesta()
myCar instanceof Fiesta //true
myCar instanceof Car //true

in

Return true if the first operand is a property of the object passed on the right, or a property of one of its ancestors in its prototype chain:

class Car {
  constructor() {
    this.wheels = 4
  }
}
class Fiesta extends Car {
  constructor() {
    super()
    this.brand = 'Ford'
  }
}

const myCar = new Fiesta()
'brand' in myCar //true
'wheels' in myCar //true

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:

let 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 = 2 + 5 / 2 % 2
const a = 2 + 2.5 % 2
const a = 2 + 0.5
const a = 2.5

I didn’t talk about bitwise operators, which are a bit too advanced for this course level, and other operators like void or other very rarely used ones.

Statements

If expressions are single units of JavaScript that the engine can evaluate, statements can contain one or more different expressions, and are executed by the engine to perform an operation.

Programs are composed by multiple statements. Statements can span over multiple lines.

Just like with expressions, JavaScript has a whole different set of statements:

  • expression statements
  • declaration statements
  • control flow statements
  • loop statements
  • miscellaneous statements

Let’s dive into the details.

Separating statements

Statements can end with an optional semicolon ; . Using it, you can have multiple statements on a single line. I normally don’t use semicolons, but you can use it whenever a statement ends.

Expression statements

An expression on its own is also a statement:

2
0.02
'something'
true
false
this //the current scope
undefined
i //where i is a variable or a constant
1 / 2
i++
i -= 2
i * 2
'A ' + 'string'
[] //array literal
{} //object literal
[1,2,3]
{a: 1, b: 2}
{a: {b: 1}}
a && b
a || b
!a
object.property //reference a property (or method) of an object
object[property]
object['property']
new object()
new a(1)
new MyRectangle('name', 2, {a: 4})
function() {}
function(a, b) { return a * b }
(a, b) => a * b
a => a * 2
() => { return 2 }
a.x(2)
window.resize()

Declaration statements

With a declaration statement you assign a value to a variable name.

Examples:

var i = 0
let j = 1
const k = 2

//declare an object value
const car = {
  color: blue
}

Here are function declarations:

//declare a function
function fetchFromNetwork() {
  //...
}
//or
const fetchFromNetwork = () => {
  //...
}

Control flow statements

Statements can be grouped, using a block:

{
  //this is a block
  const a = 1;
  const b = 2;
}

Using this syntax, you can have multiple statements whenever JavaScript expects a single statement.

We’ll talk about control flow very soon. But in the meantime just be aware that any of the conditional control flow statements check an expression and depending on it they execute a statement, or a block:

if (condition === true) {
  //execute this block
} else {
  //execute this block
}

You can omit curly braces if you only have one statement:

if (condition === true) /* statement */ else /* another statement */

or

if (condition === true)
  /* statement */
else
  /* another statement */

but it’s generally not recommended because it’s easy to create subtle bugs in this way.

I’ll go into all the different control flow structures in the next sections.

Loop statements

Loops is a big argument as well. We’ll explore loops very soon, in the meantime just know that they work similarly to the if example above.

Some loops check an expression, and repeat a statement execution it until that expression evaluates to true.

Some other loops iterate over a list and execute a statement (or block) for each element of the list, until the list finishes.

Miscellaneous statements

return <expression>

This statement returns a value from a function, ending the function execution.

throw <expression>

Throws an exception (we’ll later see what is an exception)

try and catch

A try/catch block is used to catch exceptions. Again, we’ll see those applied later on.

try {

} catch (<expression>) {

}

use strict

This statement applies strict mode (we’ll soon see what that means)

debugger

Adds a breakpoint which the debugger can use. We’ll soon see it in practice.

Coding style

Every language has a set of rules when it comes to syntax.

When starting out, some people might add code into a file following without breaking the language rules, but without giving care and attention to the programming style .

Not because they don’t care about style,they are not experienced enough to recognize its importance.

I really believe programming is a craft. Like painting, or wood crafting, or anything that involves creativity, our programs can do many things but they should do it in style.

We have some rules that are valid across all programming languages.

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:

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. My recommendation is to 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.

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.

const test = (a, b) => a + b

const another = a => a + 2

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 .

for (initialization; condition; update) {
  statements
}

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
}

Whitespace : use whitespace wisely to improve readability: put a whitespace after a keyword followed by a ( ; before & after a binary operation ( + , - , / , * , && …); inside the for statement, after each ; to separate each part of the statement; after each , .

New lines : use new lines to separate blocks of code that perform logically related operations.

Quotes favor single quotes ' instead of double quotes " . Double quotes are a standard in HTML attributes, so using single quotes helps remove problems when dealing with HTML strings. Use template literals when appropriate instead of variable interpolation.

Come back to this lesson as you learn all the concepts I talked about, in the next lessons of the course.

Semicolons

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:

const hey = 'hey'
const you = 'hey'
const heyYou = hey + ' ' + you

['h', 'e', 'y'].forEach((letter) => console.log(letter))

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

const hey = 'hey';
const you = 'hey';
const heyYou = hey + ' ' + you['h', 'e', 'y'].forEach((letter) => console.log(letter))

Such piece of code:

(1 + 2).toString()

prints "3" .

const a = 1
const b = 2
const c = a + b
(a + b).toString()

instead raises a TypeError: b is not a function exception, because JavaScript tries to interpret it as

const a = 1
const b = 2
const c = a + b(a + b).toString()

Another example based on rule 4:

(() => {
  return
  {
    color: 'white'
  }
})()

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'
  }
})()

You’d think this code shows ‘0’ in an alert:

1 + 1
-1 + 1 === 0 ? alert(0) : alert(2)

but it shows 2 instead, because JavaScript per rule 1 interprets it as:

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.

Pick some rules:

  • 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

ECMAScript

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

What do they mean?

They are all referring to a standard , called ECMAScript.

ECMAScript is the standard upon which JavaScript is based , and it’s often abbreviated to ES .

Beside JavaScript, other languages implement(ed) ECMAScript, including:

  • 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 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 :smile:

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:

Edition Official name Date published
ES9 ES2018 June 2018
ES8 ES2017 June 2017
ES7 ES2016 June 2016
ES6 ES2015 June 2015
ES5.1 ES5.1 June 2011
ES5 ES5 December 2009
ES4 ES4 Abandoned
ES3 ES3 December 1999
ES2 ES2 June 1998
ES1 ES1 June 1997

People commonly refer to “ES6” when they actually mean “modern JavaScript”. In this course I will exclusively talk about modern JavaScript, so if all this ES talk is confusing, don’t worry. Just take note that every summer, JavaScript gets some new features which might be useful to your day to day, or might be a less popular thing that is only used by very advanced developers.

Comments

One of the rules of programming I mentioned in section 1 is commenting.

In JavaScript comments start with a double slash // or are included in a block delimited by /* and */ . The first is for one-line comments, the last is for multiline comments:

//this is a comment

/*
this is also a comment
*/

The first comments everything that’s on its right, on the current line.

The second can span over multiple lines and needs to be closed.

You can also use the second one on a single line:

/* this is also a comment */

What makes a good comment? It’s hard to say because it’s not an exact science, but we have some rules.

Make your comment explain the why, not the what

Your code should be well readable enough by your practice of choosing meaningful and self-explanatory variable names, function names and object names.

Comments should explain why you decided to write the code in that way. Maybe there is some constrain that was imposed beforehand and you have to workaround it and the code alone does not fully explain the matter.

Make sure the comments and the code agree

This might seem obvious. It’s easy when you start from a blank slate and you write the comments and the code together.

It’s also easy to forget to update a comment when you change something in the code. So keep this in mind, or you - or anyone working on the same codebase - will ask this question: “is the code correct and the comment is wrong, or is it wrong the code and the comment is right?”

Make every comment count. Don’t over-comment, but comment when necessary

When you see a file with more comments than code, it’s either generating the documentation automatically from the code - so there’s a reason for all those comments (example from Lodash).

But if not, you have to ask yourself some questions. Who is likely to read 20 lines of comment when reading the file?

On the other hand, if there’s 100 lines of code and 2 lines of comment before all the code blob, you are much more likely to pay it attention.

Scarcity is key here.

Of course make every comment count. You want it to be meaningful, to hold some valuable information.

Scope

Scoping is the set of rules that’s defined in a programming language to determine the value of a variable.

JavaScript uses lexical scoping , which means that the value of a variable is defined by its position when it’s written. Not when it’s called, which is something that happens with the alternative, dynamic scoping .

Scope is the set of variables that’s visible to a part of the program.

We have a global scope, block scope and function scope. If a variable is defined outside of a function or block, it’s attached to the global object and it has a global scope, which mean it’s available in every part of a program.

I already mentioned in the variables lesson that there is a very important difference between var , let and const declarations.

A variable defined as var inside a function is only visible inside that function. Just like function parameters.

A variable defined as const or let on the other hand is only visible inside that block where it resides.

It’s important to understand that a block (identified by a pair of curly braces) does not define a new scope for var , but it does for let and const . A new scope for var is only created when a function is created, because var does not have block scope, but function scope.

Inside a function, any var 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 variable declarations on top (something that is called hoisting ). To avoid confusion, always declare var variables at the beginning of a function.

This is what I mean. Even if you declare a var variable at the end of a function, its declaration is moved to the top:

function run() {
  console.log(`${name}`)
  var name = 'Flavio'
}

run()

This prints “undefined”, because what actually happens is:

function run() {
  var name;
  console.log(`${name}`)
  name = 'Flavio'
}

run()

let and const do not “suffer” from hoisting. If you use one of them in the above example, you’d get an error: ReferenceError: name is not defined .

In JavaScript, 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, and this is called closure (we’ll talk more extensively about this later).

There is one little thing you need to be aware of. In non-strict mode, if you use a variable without declaring it, wherever you do that, that variable is going to be attached to the global scope. Which can be a bad source of bugs. So, make sure you always declare variables before using them. Just be aware of this, but it’s just another reason to use strict mode by default, which solves this issue. We’ll talk about strict mode later.

Remember: any variable defined in a function (or block) with the same name as a global variable takes precedence over the global variable, shadowing it.

This prints undefined :

var name = 'Roger'

function run() {
  console.log(`${name}`)
  var name = 'Flavio'
}

run()

and this raises an error ReferenceError: name is not defined :

let name = 'Roger'

function run() {
  console.log(`${name}`)
  let name = 'Flavio'
}

run()

This

this is a keyword 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!`)
  }
}

The same works in this example:

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 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'
}

const drive = function() {
  console.log(`Driving a ${this.maker} ${this.model} car!`)
}.bind(car)

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!

Using call() or apply() , at the function invocation step:

const car = {
  maker: 'Ford',
  model: 'Fiesta'
}

const drive = function(kmh) {
  console.log(`Driving a ${this.maker} ${this.model} car at ${kmh} km/h!`)
}

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
}

You can bind it using

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 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 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'

const name = 'Flavio'
const hello = () => 'hey'

//...

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

Accidental global variables

If you assign a value to an undeclared variable, JavaScript by default creates that variable on the global object:

;(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 undefined = 1(() => {
  'use strict'
  undefined = 1
})()

The same applies to Infinity, NaN, eval , arguments and more.

In JavaScript you can define a property of an object to be not writable, by using

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
  }
)()

Sloppy mode allows to extend a non-extensible object:

const car = { color: 'blue' }
Object.preventExtensions(car)
car.model = 'Fiesta'(
  //ok

  () => {
    '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

Strict mode fails in all those cases:

;(() => {
  '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 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() { [native code] }
  }
)()

Function arguments with the same name

In normal functions, you can have duplicate parameter names:

(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

Note that arrow functions always raise a SyntaxError in this case:

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

Events

In this lesson we’ll cover events, event handlers, the Event object, event bubbling and capturing, propagation and event throttling

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:

Inline event handlers

This style of event handlers is very rarely used today, due to its constrains, but it was the only way in the JavaScript early days:

<a href="site.com" onclick="dosomething();">A link</a>

DOM on-event handlers

This is common when an object has at most one event handler, as there is no way to add multiple handlers in this case:

window.onload = () => {
  //window loaded
}

It’s most commonly used when handling XHR requests:

const xhr = new XMLHttpRequest()
xhr.onreadystatechange = () => {
  //.. do something
}

You can check if an handler is already assigned to a property using if ('onsomething' in window) {} .

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:

const link = document.getElementById('my-link')
link.addEventListener('click', event => {
  // link clicked
})

This object contains a lot of useful properties and methods, like:

  • target , the DOM element that originated the event
  • type , the type of event
  • stopPropagation() , called to stop propagating the event in the DOM

(see the full list).

Other properties are provided by specific kind of events, as Event is an interface for different specific events:

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 a readable format ( Escape , Enter and so on) by checking the key property:

window.addEventListener('keydown', event => {
  // key pressed
  console.log(event.key)
})

On a mouse event we can check which mouse button was pressed:

const link = document.getElementById('my-link')
link.addEventListener('mousedown', event => {
  // mouse button pressed
  console.log(event.button) //0=left, 2=right
})

Event bubbling and event capturing

Bubbling and capturing are the 2 models that events use to propagate.

Suppose you DOM structure is

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

A click event on a will propagate to section and then body .

You can stop the propagation by calling the stopPropagation() method of an Event, usually at the end of the event handler:

const link = document.getElementById('my-link')
link.addEventListener('mousedown', event => {
  // process the event
  // ...

  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 throttling later)

Keyboard events

keydown fires when a keyboard button is pressed (and any time the key repeats 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:

let cached = null
window.addEventListener('scroll', event => {
  if (!cached) {
    setTimeout(() => {
      //you can access the original event at `cached`
      cached = null
    }, 100)
  }
  cached = event
})

Unicode and UTF-8

If not specified otherwise, the browser assumes the source code of any program to be written in the local charset, which varies by country and might give unexpected issues. For this reason, it’s important to set the charset of any JavaScript document.

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.

This is what the Unicode standard says:

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

This is what the W3C says:

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:

Content-Type: application/javascript; charset=utf-8

If this is not set, the fallback is to check the charset attribute of the script tag:

<script src="./app.js" charset="utf-8">

If this is not set, the document charset meta tag is used:

...
<head>
  <meta charset="utf-8">
</head>
...

The charset attribute in both cases is case insensitive (see the spec)

All this is defined in RFC 4329 “Scripting Media Types”.

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.

How JavaScript uses Unicode internally

While a JavaScript source file can have any kind of encoding, JavaScript will then convert it internally to UTF-16 before executing it.

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.

Using Unicode in a string

A unicode sequence can be added inside any string using the format \uXXXX :

const s1 = '\u00E9' //é

A sequence can be created by combining two unicode sequences:

const s2 = '\u0065\u0301' //é

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:

const s3 = 'e\u0301' //é
s3.length === 2 //true
s2 === s3 //true
s1 !== s3 //true

Normalization

Unicode normalization is the process of removing ambiguities in how a character can be represented, to aid in comparing strings, for example.

Like in the example above:

const s1 = '\u00E9' //é
const s3 = 'e\u0301' //é
s1 !== s3

ES6/ES2015 introduced the normalize() method on the String prototype, so we can do:

s1.normalize() === s3.normalize() //true

Emojis

Emojis are fun, and they are Unicode characters, and as such they are perfectly valid to be used in strings:

const s4 = '🐶'

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

The :dog: symbol, which is U+1F436 , is traditionally encoded as \uD83D\uDC36 (called surrogate pair). There is a formula to calculate this, but it’s a rather advanced topic.

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.

:couple_with_heart_woman_woman: is created combining :woman: ( \uD83D\uDC69 ), :heart:‍ ( \u200D\u2764\uFE0F\u200D ) and another :woman: ( \uD83D\uDC69 ) in a single string: \uD83D\uDC69\u200D\u2764\uFE0F\u200D\uD83D\uDC69

There is no way to make this emoji be counted as 1 character.

Get the proper length of a string

If you try to perform

'👩‍❤️‍👩'.length

You’ll get 8 in return, as length counts the single Unicode code points.

Also, iterating over it is kind of funny:

Iterating an emoji

And curiously, pasting this emoji in a password field it’s counted 8 times, possibly making it a valid password in some systems.

How to get the “real” length of a string containing unicode characters?

One easy way in ES6+ is to use the spread operator:

;[...'🐶'].length //1

You can also use the Punycode library by Mathias Bynens:

require('punycode').ucs2.decode('🐶').length //1

(Punycode is also great to convert Unicode to ASCII)

Note that emojis that are built by combining other emojis will still give a bad count:

require('punycode').ucs2.decode('👩‍❤️‍👩').length //6
[...'👩‍❤️‍👩'].length //6

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.

ES6 Unicode code point escapes

ES6/ES2015 introduced a way to represent Unicode points in the astral planes (any Unicode code point requiring more than 4 chars), by wrapping the code in graph parentheses:

'\u{XXXXX}'

The dog :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.

Encoding ASCII chars

The first 128 characters can be encoded using the special escaping character \x , which only accepts 2 characters:

'\x61' // a
'\x2A' // *

This will only work from \x00 to \xFF , which is the set of ASCII characters.

Modules

ES Modules is the ECMAScript standard for working with modules.

A bit of history: while Node.js has been using the CommonJS standard for 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 ES2015 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:

import package from 'module-name'

while CommonJS uses

const package = require('module-name')

A module is a JavaScript file that exports one or more values (objects, functions or variables), using the export keyword. For example, this module exports a function that returns a string uppercase:

uppercase.js

export default str => str.toUpperCase()

In this example, the module defines a single, default export , so it can be an anonymous function. Otherwise it would need a name to distinguish it from other exports.

Now, any other JavaScript module can import the functionality offered by uppercase.js by importing it.

An HTML page can add a module by using a <script> tag with the special type="module" attribute:

<script type="module" src="index.js"></script>

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:

import toUpperCase from './uppercase.js'

and we can use it:

toUpperCase('test') //'TEST'

You can also use an absolute path for the module import, to reference modules defined on another domain:

import toUpperCase from 'https://flavio-es-modules-example.glitch.me/uppercase.js'

This is also valid import syntax:

import { toUpperCase } from '/uppercase.js'
import { toUpperCase } from '../uppercase.js'

This is not:

import { toUpperCase } from 'uppercase.js'
import { toUpperCase } from 'utils/uppercase.js'

It’s either absolute, or has a ./ or / before the name.

Other import/export options

We saw this example above:

export default str => str.toUpperCase()

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 }

Another module can import all those exports using

import * from 'module'

You can import just a few of those exports, using the destructuring assignment:

import { a } from 'module'
import { a, b } from 'module'

You can rename any import, for convenience, using as :

import { a, b as two } from 'module'

You can import the default export, and any non-default export by name, like in this common React import:

import React, { Component } from 'react'

You can see an ES Modules example here: https://glitch.com/edit/#!/flavio-es-modules-example?path=index.html

CORS

Modules are fetched using CORS. This means that if you reference scripts from other domains, they must have a valid CORS header that allows cross-site loading (like Access-Control-Allow-Origin: * )

What about browsers that do not support modules?

Use a combination of type="module" and nomodule :

<script type="module" src="module.js"></script>
<script nomodule src="fallback.js"></script>

Conclusion

ES Modules are one of the biggest features introduced in modern browsers. They are part of ES6 but the road to implement them has been long.

We can now use them! But we must also remember that having more than a few modules is going to have a performance hit on our pages, as it’s one more step that the browser must perform at runtime.

Webpack is probably going to still be a huge player even if ES Modules land in the browser, but having such a feature directly built in the language is huge for a unification of how modules work client-side and on Node.js as well.

Quiz

Welcome to the quiz! Try to answer those questions, which cover the topics of this module.

You can also write the question/answer into the Discord chat, to make sure it’s correct - other students or Flavio will check it for you!

  • describe what is a value, a type and a variable. And the differences between those 3 concepts
  • what does it mean that JavaScript is loosely typed?
  • what is the difference between let , const and var variable declarations?
  • explain the concept of this in JS
  • describe in your words what is scope
  • do you think enabling strict mode is important? Why?
  • why do we need to stop the propagation of events?