Skip to main content
JavaScript

Hoisting in JavaScript – a complete guide

By February 1, 2022July 20th, 2022No Comments
var, let and const hoisting in JavaScript

Let’s delve into hoisting in JavaScript! During compilation, microseconds before code is executed, variable and function declarations are “hoisted” i.e. they are added to memory inside a data structure called “lexical environment”. The official ES6 documentation describes the lexical environment as:

“Lexical Environment is a specification type used to define the association of Identifiers to specific variables and functions based upon the lexical nesting structure of ECMAScript code.”

Conceptually, this refers to a data structure containing variable and function declarations that are initialized with pre-defined values:

Name of variable/function Pre-defined  value

Adding variable and function declarations to memory during compilation before they get executed is called “hoisting”. All variable declarations and function declarations are scanned for and placed in memory at the start of their enclosing scope {}. This mechanism of hoisting is what allows you to make forward references to variable and function declarations, as they’re saved in memory with pre-defined values. Practical examples are provided as we move on in the next section.

Code gets executed during runtime, which means all declarations, assignments, and function calls will be executed then. We’ll explore hoisting in reference to var, let, const and function declarations in the following sections. This will make the concept clearer.

Note:

Only declarations get hoisted, not initializations or assignments

Var hoisting

 

To understand variable hoisting better, here is a small summary of the variable declaration, initialization and assignment phases. This is commonly known as “making a variable”.

Declaration

Variable declaration refers to the declaration of a variable with a keyword such as var. For example:

var x;

Initialization

During declaration, variables are automatically assigned a value of undefined. This is called initialization:

var x = undefined;

Assignment

Assignment refers to assigning a value to a variable with the assignment operator (=). Let’s assign a numeric value of 100 to a variable called x:

var x = 100;

And this brings us to the next point, how are var variables hoisted in memory?

var variables are hoisted in memory with a starting value of undefined. For example:

var icecream = 'Maple Syrup';

Conceptually, it will be stored in memory like so:

Variable Name Value
icecream  undefined

Therefore, which is whjy when you try and access a var variable before its declaration, the undefined type is returned:

console.log(icecream); // undefined
var icecream = 'Maple Syrup';

undefined is logged to the console.  The following bullet points will explain why:

  • The variable declaration var icecream is hoisted to the top of its scope, in this case, global scope. It is initialized with a starting value of undefined
  • Only the variable declaration is moved, not its assignment (= ‘Maple Syrup’)
  • Since only the declaration var ice-cream is hoisted to the top, the console.log statement will hence return undefined.
  • When code is executed at runtime, the undefined value will be overwritten with the value assigned by us which is ‘Maple Syrup’

The above bullet points can be demonstrated in code format as:

var icecream;
console.log(icecream);// undefined
icecream = 'Maple Syrup';

After hoisting, during execution, the value of var variable will be overwritten with the value assigned by a user

Let and const hoisting

let and const are stored as uninitialized values, in contrast to var which is stored as undefined.  As an example:

let city = 'Toronto';
const apple = 'Granny smith'

 

These will be stored as uninitialized:

Variable Name Value
 let, const  uninitialized

Trying to reference a let or const variable before its declaration will result in a ReferenceError getting thrown:

console.log(city);
console.log(apple);
let city = 'Toronto';
const apple = 'Granny smith';
//ReferenceError: can't access lexical declaration 'city' before initialization

Like var, let and const variables are hoisted. However, unlike var, they cannot be accessed before they are declared.  This is because they lie inside the Temporal Dead Zone (TDZ).

let and const variables are said to lie within the Temporal Dead Zone when they’re declared, but not as yet initialized in memory. These variables get hoisted, but no value is assigned to them in memory, not even undefined.

Function hoisting

 

Functions are hoisted along with a reference to the body of the entire function. For example, take a function called myFunc():

function myFunc(){
  console.log('This is myFunc');
}

The myFunc() function will be stored in memory during compilation like so:

Function name Value
myFunc console.log(‘This is myFunc’);

Therefore, referencing a function before its declaration behaves as it would when calling a function after its declaration:

myFunc();
function myFunc(){
  console.log('This is myFunc');
}

The function will execute before its declaration and the following will be logged to the console:

"This is myFunc"

Having understood the hoisting mechanism for var, let, const and function declarations. Let’s examine the precedence order for these.

Precedence for variable and function hoisting

The order of precedence for hoisting is:

        1.  Variable assignment

        2.  Function declaration

        3. Variable declaration

For example:

function fruit(){
  console.log('peaches, mandarins, grapes');
var fruit = 'apples';
}
console.log(typeof(fruit)); // "string"

 

Variable assignment takes precedence over function declaration during hoisting. Hence, the statement typeof(fruit) will log “string” to the console.

Moving on, function declarations takes precedence over variable declarations, as we can see from the example below:

var fruit;

function fruit(){
}

console.log(typeof(fruit)); //"function"

The typeof operator takes fruit as an argument as returns “function” due to function declaration taking precedence over variable assignment during hoisting.

An important point to keep in mind is that let and const cannot be re-declared in the same scope. Let’s refactor the above statement to use let instead of var:

let fruit;
function fruit() {
}
console.log(typeof(fruit)); //SyntaxError: redeclaration of let fruit

You cannot re-declare let variables in the same scope, therefore, a syntaxError is returned:

SyntaxError: redeclaration of let fruit

This demonstrates one of the advantages of using let and const over var.

While writing bug free code that can be used in production is the ultimate goal, errors can arise on the way due to quirks such as hoisting. Learning to handle errors effectively will greatly increase your productivity as a developer.

Leave a Reply