Hoisting in JavaScript with var
Hoisting is the phenomenon in JavaScript by which we can access variables and functions without any error even before initializing them.
Execution Context
Everything in JavaScript happens inside an execution context. Whenever a JavaScript program is run, a Global Execution Context is created. Every execution context has two phases:
Memory Creation Phase: This is where all the variables and functions are stored as key-value pairs.
Code Execution Phase: This is where code is executed one line at a time.
Let's understand this through an example.
var n = 2;
function printNum(num) {
console.log(num);
}
printNum(n);
[Note: we're using var here.
Variables defined with let and const are hoisted to the top of the block,
but not initialized. Meaning the block of code is aware of the variable,
but it cannot be used until it has been declared.
Using let or const before declaration will result in Uncaught RefereceError.]
During the memory creation phase, JS skims through the entire code and allocates memory to variables and functions, which are stored as keys.
The corresponding value for variables is undefined, and for functions its the entire code of function.
#Memory Creation phase
n: undefined
printNum: ƒ printNum(num){console.log(num);}
During the code execution phase, JS goes through the code once again and executes it line by line. Variables are assigned the said values. As for functions, whenever JS finds a function call, a new execution context is created. This new execution context goes through a similar memory creation and code execution phase. Once the function is executed, the execution context created for executing it is deleted. JS maintains a call stack for handling the creation and deletion of exection contexts.
#Code Execution Phase
n: 2
printNum: new execution context created
Execution context for printNum
#Memory Creation phase
num: undefined
#Code Execution phase
num: 2
After the program is executed, the global execution context is deleted.
Hoisting
Now that you know how JS runs behind the scene, let's understand hoisting.
var n = 2;
function hello() {
console.log("Hello World!");
}
hello();
console.log(n);
Output
Hello World!
2
hello();
console.log(n);
var n = 2;
function hello() {
console.log("Hello World!");
}
Output
Hello World!
undefined
Explanation
Function doesn't get affected as a new execution context is created for it.
In the code execution phase of GEC, n is still undefined when it's printed.
GEC -> Global Execution Context
hello();
console.log(n);
function hello() {
console.log("Hello World!");
}
Output
Hello World!
Uncaught ReferenceError: n is not defined
console.log(hello);
function hello() {
console.log("Hello World!");
}
console.log(hello);
Output
ƒ hello() {console.log("Hello World!");}
ƒ hello() {console.log("Hello World!");}
hello();
var hello = () => {
console.log("Hello World!");
}
Output
Uncaught TypeError: hello is not a function
Explanation
Whenever we assign a function in a variable, it behaves like a variable
and we know that variables have undefined stored in them post memory
creation phase.
So when we try to call something undefined, JS throws this error.
hello();
var hello = function() {
console.log("Hello World!");
}
Output
Uncaught TypeError: hello is not a function
var hello = function() {
console.log("Hello World!");
}
hello();
Output
Hello World!
Summary
Hoisting is the phenomenon in JavaScript by which we can access variables and functions without any error even before initializing them.
As long as we don't assign a function to another variable, calling it before or after it's definition yields the same output.
When we try to access a variable of var type before it's declaration, we get undefined.
When we try to access a variable of let/ const type before it's declaration, we get an error
Uncaught ReferenceError: <variable-name> is not defined
When we assign a function to a variable and try to call that function before its declaration, we get an error
Uncaught TypeError: <variable-name> is not a function