Codementor Events

Hoisting in JavaScript

Published Mar 22, 2021Last updated Jul 15, 2022
Hoisting in JavaScript

Hoisting is a fundamental concept of the JavaScript language. It's also one of those topics which is frequently asked in interviews.

While the concept is very simple it is mostly misunderstood. Let's understand hoisting with the help of some examples.

Consider this example:

var source = "tweaked.dev";
console.log(source);

function printMe() {
  console.log("from printMe:" + source);
}

printMe();
// Output:

// tweaked.dev

// from printMe:tweaked.dev

It's pretty straight forward right?

The code is executed line by line.

Let's take a look at what's happening:

  • The variable source is created and assigned a value "tweaked.dev".
  • console.log statement is encountered and the value of source is printed on the console → tweaked.dev
  • Similarly the next line is printMe function definition. Followed by the actual call to the printMe() function. This results in the statements inside the function being executed and the string "from printMe:tweaked.dev" being printed to the console.

But is code execution really this straight forward in JavaScript?

Take a look at this variation of the same example.

console.log(source);

printMe();

function printMe() {
  // function definition
  console.log("from printMe:" + source);
}

var source = "tweaked.dev"; // variable declaration and value assignment

printMe();

Here we are logging the variable source to console even before it is declared.

Also we are calling the function printMe before it is being defined.

Will this code throw an error?

What do you think is the expected output for this example?

Take a moment to think before you see the output below.

console.log(source);

printMe();

function printMe() {
  console.log("from printMe:" + source);
}

var source = "tweaked.dev";

printMe();
// Output:

// undefined

// from printMe:undefined

// from printMe:tweaked.dev

In most programming languages this would throw an exception.

Well, JavaScript allows this. How? Due to hoisting.

Hoisting is a feature in javascript where a variable can be used before it is declared. Similarly a function can also be called before it is defined.

To understand how hoisting works we need to understand the execution context.

Execution Context

Execution context is the environment which is prepared by the javascript engine in order to execute the code that you write.

Simply put it is the information which is necessary in order to execute the code.

The execution context is created in two phases :-

Creation Phase

  • Code is scanned/parsed for variables and functions.
  • Space is reserved for variables in memory.
  • Function definitions are loaded in memory.

Execution Phase

  • Code is executed statement by statement with the information from the creation phase.
  • Variables are assigned values.
  • Functions are executed.

Let's see the execution context phases for the example illustrated earlier:

Creation phase image

It finds the variable source and loads it in the memory. Since the code is not yet executed it's value is undefined.

The function printMe is also loaded in memory.

Execution phase image

To check if you've clearly understood the concept of hoisting and execution context, let’s consider another example:

source = 5;

console.log(source);

printMeFnExp();

// function expression syntax

var printMeFnExp = function printMe() {
  console.log("from function:" + source);
};

var source;
// Output:
// 5
// Uncaught TypeError: printMeFnExp is not a function

Surprised by the error in the output? You shouldn't be.

For a better understanding, take a look at the diagrams below.

Creation Phase 2nd Example Image

  • The variables source and printMeFnExp are loaded in memory.
  • The function printMe is loaded in the memory.

Take a closer look at printMeFnExp - it's a function expression. This indicates that it is variable whose value is pointing to a function.

In simple terms, the function is assigned to a variable. To call the function which is assigned to a variable, we need to write "functionName" followed by brackets.

Example: printMeFnExp()

Execution Phase 2nd Example Image

  • The value 5 is assigned to source.
  • 5 is logged to the console.
  • printMeFnExp is called. However it throws an error - Uncaught TypeError: printMeFnExp is not a function.

This is happening because the variable was hoisted, but it's initial value is still undefined. Thus we get an error for trying to call a function on an undefined value.

The statement which assigns the printMe function reference to printMeFnExp has not been executed.

To fix this error, see the code changes below:

source = 5;

console.log(source);

// function expression syntax

var printMeFnExp = function printMe() {
  console.log("from function:" + source);
};

printMeFnExp();

var source;

// Output:

// 5

// from function:5

Here the printMeFnExp has been assigned the printMe function reference. Thus it is now possible to invoke the function expression like this - printMeFnExp();

Note: There is more to the execution context than mentioned in this article. I've covered just enough for you to understand hoisting.

Exceptions to hoisting

Hoisting works differently if the variable declaration is done using let or const.

In case of var the value is initialized to undefined during the creation phase.
However in case of let and const the value is only intialized during the execution phase.

See the example below:

console.log(source);

printMe();

function printMe() {
  console.log("from printMe:" + source);
}

let source = "tweaked.dev";
// Output:

// Uncaught ReferenceError: source is not defined

Since the value of source is not initialized during the creation phase, source has no reference to the value in memory.
Due to this a reference error is thrown for statment console.log(source);

This concept is also known as Temporal Dead Zone. It means that a variable cannot be accessed until it is declared.

Let's look at the same example with this knowledge.

// Temporal Dead Zone
///////////////////////////////////////////////
  console.log(source);                      ///
  printMe();                                ///  
                                            ///   
  function printMe() {                      ///
    console.log("from printMe:" + source);  ///
  }                                         ///
                                            ///
///////////////////////////////////////////////
let source = "tweaked.dev"; // Temporal Dead Zone Ends

Here the // lines represent the Temporal Dead Zone for the source variable. We will get a reference error if we try to access source within this block.

Below is the correct usage of let:

let source = "tweaked.dev";
console.log(source);

function printMe() {
  console.log("from printMe:" + source);
}

printMe();

During the execution phase if no value is provided along with declaration, then the value is considered as undefined.

Refer the example:

let source; // declaration
console.log(source);

source = "tweaked.dev"; // initialization

function printMe() {
  console.log("from printMe:" + source);
}

printMe();
// Output:

// undefined

// from printMe:tweaked.dev

Example with const:


const source;

console.log(source);


// Output:

// Uncaught SyntaxError: Missing initializer in const declaration

A const indicates a constant value. This value cannot be changed during code execution.
Therefore it makes sense that it requires an initializer value at the time of declaration.

Correct usage of const:


const source = "tweaked.dev";
console.log(source);

// Output:

// tweaked.dev

Conclusion

Coding best practices suggest to declare variables at the beginning of the code block.
It is also preferable to use let and const for variable declaration. This enhances code readability.

While the usage of variable declaration using var has become outdated, it is still important to know this concept since you might still encounter this pattern in some of the existing code bases or even an interview. There is a chance you might even have to refactor such a code with the latest JavaScript features.

Knowledge of hoisting will help you avoid any bugs and confusion related to variable declaration and its usage.

Please feel free to ask your queries or doubts.

You can read more articles from me on my personal blog - www.gauravsen.com/blog

Discover and read more posts from Gaurav Sen
get started