Hoisting in JavaScript

Hoisting is a JavaScript behavior, where JavaScript creates the variable and functions before executing the script. This allows us to use the function or variable even before we declare them. In this tutorial, let us learn what hoisting is and how it works in JavaScript.

What is Hoisting

Most sites define Hoisting as JavaScript’s behavior where it moves all declarations to the top of the current scope. Although It is what appears to be happening it is not true.

But first, let us understand what is hoisting using an example.

Take a look at the following example, where we try to access a variable before its declaration.

Since JavaScript is an Interpreted language, we expect it to read each line of code and execute it before moving on to the next line. Hence it is natural to assume that the console.log statement will throw an error. Because when JavaScript executes the console.log statement, the variable a does not exist yet.

However, the console.log output will be undefined.

The undefined value indicates JavaScript already created the variable and also assigned undefined value it. It has created it even though it is yet to execute the line where we have actually created it.

Here it appears that JavaScript has moved the declaration of the variable to the top of the current scope as shown in the code below.

The same goes for functions. The following code work without any error, although we execute the sayHi function even before we declare it.

Here also it appears that JavaScript moves the sayHi function to the top of the current scope before execution as shown below.

This behavior of JavaScript where it appears to move all declarations to the top of the current scope is Hoisting.

How does it work

JavaScript is an interpreted language. The JavaScript engine is responsible for interpreting each line of code and executing it.

But JavaScript also compiles the code before executing it. In fact, it executes the code in two phases

Compilation Phase: (or Preparation Phase) where JavaScript runs through the code, Parses it, creates execution context, and does a lot of other stuff.

Execution Phase: where the JavaScript interpreter actually runs the code line-by-line.

As part of the compilation phase, JavaScript looks for all declarations and creates the variables and functions in memory. Hoisting is a side effect of such behavior

In the example below, the JavaScript compiler reads the line var a=1 and creates the variable a in the memory. It assigns the value undefined to the created variable.

Note that the statement var a=1 actually consists of two statements. One is declaration i.e. var a and the other one is an assignment a=1. The JavaScript compiler only processes the declaration and not the assignment.

Once the Compiler completes its work, the JavaScript engine begins executing the code one line at a time. It first executes the code console.log(a). Since the variable a is already created and initialized with undefined, it prints undefined in the console window.

JavaScript also hoists the variable declared with let & const. Modify the above example and replace the var with let. Now instead of undefined we get Cannot access [variable name] before initialization.

That is because, in the case of let (also const), the JavaScript Hoists the variable but does not initialize it with any value.

Variable Life Cycle

In JavaScript variables also go through three stages during the compilation & execution.

Declaration: phase is where JavaScript registers the variable in the scope.

Initialization: phase is where the engine allocates memory to the variable and assigns an initial value of undefined

Assignment: is where JavaScript assigns value to the variable.

Whether these phases happen at compile-time or at execution time depends on how we declared the variable. The following table shows the relationship between the compilation & execution phase with the let, var, const & function declarations

KeywordDeclarationInitializationAssignment
varCompilationCompilationExecution
let & constCompilationExecutionExecution
functionCompilationCompilationCompilation

The summary of the above table is

  • For a var variable declaration and Initialization are hoisted. But not the assignment.
  • For a let & const variable declaration is hoisted. But not initialization and assignment.
  • For a function declaration, initialization, and assignment are hoisted.

Error Messages

The error messages that JavaScript throws when we try to access a variable give us a hint about the variable’s current state.

Before Declaration

JavaScript throws a "[variable name] is not defined" error whenever we try to access an undeclared variable. In the following example, we have not declared the variable a hence it throws the error "a is not defined".

Before Initialization

The JavaScript hoists the variables declared with let or const but does not initialize them. When we try to access a variable that is not initialized, we get Cannot access [variable name] before initialization error.

After Initialization

JavaScript assigns undefined to every variable which we have declared using var but not yet initialized. In the following code, we have declared the variable but have not assigned any value to it. Hence the code outputs the undefined.

Remember that we can also assign undefined to a variable.

Hence for a variable

  • that we have not declared the JavaScript throws [variable name] is not defined error.
  • declared but not initialized, then we get the error Cannot access [variable name] before initialization
  • declared & Initialized but not yet assigned any value, then we get undefined. JavaScript does not throw any errors here.

Variable Hoisting

We already looked at the following example.

The javascript does the following in the compilation phase

  1. variable a is registered with the scope
  2. created in memory with the initial value of undefined

Then the execution begins

The compilation phase when using let.

  1. variable a is registered with the scope

Then the execution begins

During the compilation phase, the engine registers the variable with the scope. During the execution time, it assigns undefined to a when it encounters the declaration statement.

Redeclaring the Variable

The code declares the variable a after initializing it with the value 2. Here the declaration var a is hoisted at the compile time. Hence the code does not throw any errors.

But if we use let instead of var, you will get the Cannot access 'a' before initialization error. This is because the statement let a is hoisted but is not initialized. Hence when we access it on the first line, we get the error.

Undeclared variables

The following code throws the a is not defined error. This is because hoisting works only for the declaration made using the var, let & const. The statement a=2 does not declare the variable hence not hoisted.

The following code works fine because the statement a=2 creates the global variable at the time of the execution phase.

In the strict mode, you can not use undeclared variables. Hence the above code throws an error.

Nested Scopes

The variables declared inside the scope are not accessible outside of their scope.

The let variables are block-scoped. JavaScript Engine hoists them to the top of the scope in which we have declared them.

The following example let a=3 statement creates a variable a during the compile time, but within the scope of the code block. Since it is declared with let it is not initialized with undefined. Hence we get the error.

Note that we already have a variable a declared in the global scope. But console.log will not use it as the a declared in the code block overrides it.

If you remove the let declaration, then the console.log will use the a from the global scope.

But var variable ignore the block scopes. Hence the following code works without error.

The Functions also create nested scopes. Hence the variable a inside the function is local to the function and hoisted within the function.

Here variable a is hoisted by JavaScript, but the assignment never happens because the If condition evaluates to false.

Function Hoisting

JavaScript hoists the Function declarations. In the following example, the compilation phase creates the function variable sayHi, initializes it, and assigns it with the function.

Function assignment

JavaScript engine only hoists the Function declarations and not assignments. In the following example, we assign the function to a sayHi variable using var. Since the sayHi uses var for its declaration, JavaScript creates it with the initial value of undefined.

You can verify that by checking the value of sayHi

Naturally, a let declaration throws an error.

Function Declaration vs Var Declaration

When a function and a variable have the same name, JavaScript ignores the variable declaration at the compiling step. The function always has a higher priority.

In the following example, sayHi variable points to the function even though the next line of code declares a variable of the same name.

Note that let does not allow us the redeclare a variable. The above code with let instead of var will throw the error.

Temporal Dead Zone (TDZ)

The Term Temporal Dead Zone refers to the area in the code where the variable that we are trying to access is declared but not yet initialized. This only happens with let & const declarations because the var declarations are always initialized with undefined.

In the code below, the area code from the start of the function until the let declaration is Temporal Dead Zone for variable a. Because of Hoisting, the JavaScript engine has created the variable before the execution of the function and has yet to initialize it.

Similarly, a variable in the global scope TDZ starts from the top.

Avoid Hoisting

Hoisting makes it difficult to detect bugs in JavaScript. It makes the code confusing and difficult to read. Hence it is better to avoid it wherever possible. Here are some tips to avoid Hoisting.

  1. Avoid var. Always use let & const
  2. Always declare the variable at the top of the scope
  3. Make use of "use strict"
  4. Use function expressions instead of function declarations

References

Leave a Comment

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Scroll to Top