JavaScript and Hoisting

JavaScript and Hoisting

Js Hoisting of regular function, arrow function, and variables var, let, const

Let's start this post with a question. What will be the output of this code block?

standardFunc();
function standardFunc() {
  console.log("Am I accessible before defining?");
}

If you don't know the answer, I suggest running the code block. So, what's your response? What was the output?

regularFunc();  //"Am I accessible before defining?"
function regularFunc() {
  console.log("Am I accessible before defining?");
}

Regular functions can be used before they are declared.

But how is this possible?

To know the answer to this question we must delve deeper into the Js interpreter's operation.

When you execute your JavaScript code, the interpreter goes through the code twice. The first run is also referred to as the compile run. The second run is where it actually executes your code by going through it line by line, doing the assignments, calling the functions, and so on. Now during this compile run of the Js compiler, declaration of all variables, functions, and classes to the top of their respective scopes. Take a look at the code block below. Before compilation.

//Before compilation
standardFunc();
function standardFunc() {
  console.log("Am I accessible before defining?");
}

After compilation of the code block.

//After compilation
function standardFunc() {
  console.log("Am I accessible before defining?");
}
standardFunc();

Please note that this is an oversimplification of what happens during compilation.

This is Hoisting, the moving of variable, function, and classes declaration to the top of their respective scopes. Let's take a loop at the MDN definition of Hoisting.

" JavaScript Hoisting refers to the process whereby the interpreter appears to move the declaration of functions, variables, or classes to the top of their scope, prior to execution of the code. "

Now let's look at how Hoisting behaves with the variable declaration.

var declaration

Time for another question. What will be the output of the below code?

console.log( demoVar );
var demoVar = "variable declaration gets hoisted"

If you don't know the answer, I suggest running the code block. You will be surprised. You would believe that because every variable is hoisted, the demoVar will be hoisted as well, and the message "variable declaration is hoisted" will be reported.

Wrong, the output will be undefined.

var variable declarations are hoisted, and auto initialized to undefined.

When it has been hoisted, the code block will become -

//After hoisting
var demoVar = undefined
console.log(demoVar)
demoVar = "variable declaration gets hoisted"

As you can see demoVar is just being declared and auto initialized to undefined and accessing demoVar before its actual initialization gives "undefined".

let & const

Time for another question.

console.log( hoistedString, hoistedNumber )
let hoistedString = "am I hosted?"
let hoistedNumber = 123

What will be the output of the above code block?

There will be no output an error will be thrown instead, specifically a reference error. When a variable is declared using let and const it is uninitialized. Hence it gives a reference error. It will only get initialized when the let and const statement is evaluated, everything before (above) is called the temporal dead zone. You can read more about the temporal dead zone in the MDN docs.

So that means that trying to access the let or const declared variable before initializing it will result in a Reference error.

Arrow Function & Function expression

We know that we can call a regular function before declaring it.

But what about arrow functions and function expression?

Arrow Function

arrowFunc() //ReferenceError: arrowFunc is not defined
const arrowFunc = () => {
  console.log("Am I hoisted?")
}

Function Expression

greeting(); // TypeError: greeting is not a function

console.log(greeting); // undefined

var greeting = function greeting() {
  console.log("Hello!");
};

Both function expression and arrow function behave in a similar way and cannot be called before initilization.

Conclusion

According to good code practice, you should always initialize the function or variable before using it. Using let, const instead of var, and arrow function instead of regular function will automatically solve bugs that may result due to hoisting.