Hoisting in JavaScript

Before we dive into what Hoisting is let us go through an example and understand this phenomenon.

INPUT -->
getName();
console.log(x);
var x = 7;
function getName(){
  console.log("Siddhartha Sahu Blog");
}

OUTPUT -->
Siddhartha Sahu Blog
undefined

In the above piece of code, we are trying to access getName() and x before even initializing it. If we try to perform the above in other programming languages we would straight away get an error. However, JavaScript behaves differently in such cases.

Hoisting is a phenomenon in JavaScript due to which we are able to access variables and functions even before they are initialized or allocated any value. We can access these values without getting any errors.

Let us go through another interesting example:

INPUT-->
console.log(getName);
console.log(x);
console.log(y);

var x = 7;
function getName(){
  console.log("Siddhartha Sahu Blog");
}

OUTPUT-->
ƒ getName(){
  console.log("Siddhartha Sahu Blog");
}
undefined
Uncaught ReferenceError: y is not defined at <anonymous>:3:13

As we can see, when we try to log a function that has been defined later in our code we get its function definition as the output, whereas if we try to do the same with a variable we get undefined.

But, when we try to log y, an undefined variable, in the code we get a ReferenceError which says y is not defined. This is a unique thing about JavaScript.

Why does JavaScript behave differently for functions and variables?

Aren't undefined & not defined the same?

Let us see what happens behind the scene and try to find an explanation for this anomaly.

When a JavaScript program is ran an execution context is created. The creation and execution of execution contexts have two phases:

  1. Memory Creation Phase:

    • First JavaScript skims through the whole code, line by line and allocates memory to all the variables and functions.
    • In the case of variables, it allocates undefined.
    • In the case of functions, it allocates the whole code inside the function in the memory.
  2. Code Execution Phase:

    • Javascript goes through each line of code again.
    • When the program comes across a variable it allocates the actual value to the key.
    • When it comes across an operation it performs the operation.
    • When it comes across a function declaration it skips those lines.
    • When it comes across function invocation a new execution context is created and the whole process takes place again for that function.

Now, even before the first line of code executes, memory is allocated to all variables and functions in the Memory Creation Phase. JavaScript allocates the value undefined to variables and respective function definitions to the function by skimming the code line by line. Hence when we try to log a variable or a function which is defined later in the code it will output undefined and function definition respectively because it is the value allocated to their keys in the memory allocation phase. In the case of variables which are not defined later in the code, there is no key or reference which exists for them in the memory hence a ReferenceError is thrown.

Let us go through another code example:

INPUT-->
console.log(getName);
getName();
var getName = () => console.log("Siddhartha Sahu Blog");

OUTPUT-->
undefined
Uncaught TypeError: getName is not a function at <anonymous>:1:1

Accessing a function before it is defined in the code was completely fine for previous codes. But when it comes to arrow functions, the function behaves like a variable when it comes to memory allocation. In the case of arrow functions, it allocates undefined as a placeholder to the key in the memory allocation phase.

To further read about this topic in-depth, do check out these resources:

  1. JavaScript Code Execution - GeeksforGeeks
  2. Hoisting - MDN Web Docs