JavaScript variables hoisting in details

Variables in a program are everywhere. They are small pieces of data and logic that always interact with each other: and this activity makes the application alive.
In JavaScript an important aspect of working with variables is hoisting, which defines when a variable is accessible. If you're looking for a detailed description of this aspect, then you're in the right place. Let's begin.

1. Introduction

Hoisting is the mechanism of moving the variables and functions declaration to the top of the function scope (or global scope if outside any function).

Hoisting influences the variable life-cycle, which consists of these 3 steps:

Everything looks simple and natural when these steps are successive: declare -> initialize -> use. If possible, you should apply this pattern when coding in JavaScript.

JavaScript does not follow strictly this sequence and offers more flexibility.
For instance, functions can be used before the declaration: use -> declare.
The following code sample first calls the function double(5), and only later declares it function double(num) {...}:

The var syntax allows not only to declare, but right away to assign an initial value: var str = 'initial value'. When the variable is hoisted, the declaration is moved to the top, but the initial value assignment remains in place:

Hoisting and let

let variables are registered at the top of the block. But when the variable is accessed before declaration, JavaScript throws an error: ReferenceError: <variable> is not defined.
From the declaration statement up to the beginning of the block the variable is in a temporal dead zone and cannot be accessed.

myVariable is in a temporal dead zone from let myVariable line up to the top of the block if (value) {...}. If trying to access the variable in this zone, JavaScript throws a ReferenceError.

An interesting question appears: is really myVariablehoisted up to the beginning of the block, or maybe is just not defined in the temporal dead zone (before declaration)? The exception ReferenceError is thrown also when a variable is not defined at all.
If you take a look at the beginning of the function block, var myVariable = 'Value 1' is declaring a variable for the entire function scope. In the block if (value) {...}, if let variables would not cover the outer scope variables, then in the temporal dead zone myVariable would have the value 'Value 1', which does not happen. So block variables are rough hoisted.

In an exact description, when the engine encounters a block with let statement, first the variable is declared at the top of the block. At declared state the variable still cannot be used, but it covers the outer scope variable with the same name. Later when let myVar line is passed, the variable is in initialized state and can be used.
Check also this explanation.

let expansion in the entire block protects variables from modification by outer scopes, even before declaration. Generate reference errors when accessing a let variables in temporary dead zone ensures better coding practice: first declare - then use.
Both these restrictions are an effective approach to write better JavaScript in terms of encapsulation and code flow. This is a result of lessons based on var usage, where accessing the variable before declaration is a source of misunderstanding.

Hoisting and const

Constants const are registered at the top of the block.
The constants cannot be accessed before declaration because of the temporal dead zone. When accessed before declaration, JavaScript throws an error: ReferenceError: <constant> is not defined.

const hoisting has the same behavior as the variables declared with let statement (see hoisting and let).

The code function isOdd(number) {...} is a declaration that defines a function. isOdd() verifies if a number is odd.

Hoisting and function declaration

Hoisting in a function declaration allows to use the function anywhere in the enclosing scope, even before the declaration. In other words, the function can be called from any place of the current or inner scopes (no undefined values, temporal dead zones or reference errors).

This hoisting behavior is flexible, because you can first use the function and only later declare it. Or apply the classic scenario: first declare and then use. As you wish.

The following code from the start invokes a function, and after defines it:

The code works nice because equal() is created by a function declaration and hoisted to the top of the scope.

Notice the difference between a function declarationfunction <name>() {...} and a function expressionvar <name> = function() {...}. Both are used to create functions, however have different hoisting mechanisms.
The following sample demonstrates the distinction:

addition is hoisted entirely and can be called before the declaration.
However substraction is declared using a variable statement (see 2.) and is hoisted too, but has an undefined value when invoked. This scenario throws an error: TypeError: substraction is not a function.

6. Class declarations

The class declaration defines a constructor function with the provided name and methods. Classes are a great addition introduced by ECMAScript 6.
Classes are built on top of the JavaScript prototypal inheritance and have some additional goodies like super (to access the parent class), static (to define static methods), extends (to define a child class) and more.

Hoisting and class

The class variables are registered at the beginning of the block scope. But if you try to access the class before the definition, JavaScript throws ReferenceError: <name> is not defined. So the correct approach is first to declare the class and later use it to instantiate objects.

Hoisting in class declarations is similar to variables declared with let statement (see 3.).

// Use the Company class
// Throws ReferenceError: Company is not defined
var apple = new Company('Apple');
// Class declaration
class Company {
constructor(name) {
this.name = name;
}
}
// Use correctly the Company class after declaration
var microsoft = new Company('Microsoft');

As expected, executing new Company('Apple') before the class definition throws ReferenceError. This is nice, because JavaScript suggests to use a good approach to first declare something and then make use of it.

Classes can be created using a class expression, which involves variable declaration statements (with var, let or const). Let's see the following scenario:

The class is declared with a variable statement var Square = class {...}. The variable Square is hoisted to the top of the scope, but has an undefined value until the class declaration line. So the execution of var mySquare = new Square(10) before class declaration tries to invoke an undefined as a constructor and JavaScript throws TypeError: Square is not a constructor.

7. Final thoughts

As seen in the explanations, hoisting in JavaScript has many forms. Even if you know exactly how it works, the general advice is to code variables in a sequence of declare > initialize > use. ECMAScript 6 certainly suggests this approach by the way hoisting is implemented for let, const and class. This will save you from unexpected variable appearances, undefined and ReferenceError.

As an exception, sometimes functions can be invoked before the definition: an effect of function declaration hoisting. It's useful in cases when developer needs to read quickly how functions are invoked at the top of the source file, without the necessity to scroll down and read the details about function implementation.
For example, see here how this approach increases the readability of Angular controllers.

I hope you enjoyed the reading, so do not hesitate to share it. See you in my next post :).