JavaScript Hoisting
In JavaScript, hoisting is a behavior where variable and function declarations are moved to the top of their containing scope during the execution phase.
This allows you to access variables and functions even before they are declared in the code.
Hoisting allows you to access variables and functions before they are declared in the code, but the behavior varies based on how they are declared.
Regular Function Declaration
var x = 7;
function getName() {
console.log("Sujay");
}
getName(); // Outputs: Sujay
console.log(x); // Outputs: 7
In this case, both the function declaration and the variable declaration are hoisted. When the code is executed:
The getName() function is available before its declaration because function declarations are hoisted entirely (meaning the function body is copied to memory before execution).
The variable x is hoisted, but only the declaration is moved, not the assignment. So, at the time console.log(x) is called, it logs 7 since the value was assigned before.
Calling a Function Before Declaration
getName(); // Outputs: Sujay
console.log(x); // Outputs: undefined
var x = 7;
function getName() {
console.log("Sujay");
}
In this example:
The function getName is hoisted entirely, so it can be called before its declaration, and it prints "Sujay".
The variable x is hoisted, but only the declaration (var x;) is moved to the top. Therefore, when console.log(x) is executed, it prints undefined because the assignment (x = 7) has not been reached yet during execution.
Arrow Function and Variables
When using arrow functions, the behavior changes slightly because arrow functions are expressions, not declarations. This means that they behave like variables and are hoisted as undefined:
getName(); // Throws ReferenceError: Cannot access 'getName' before initialization
console.log(x); // Outputs: undefined
var x = 7;
var getName = () => {
console.log("Sujay");
};
Here:
getName is treated as a variable, so it is hoisted with the value undefined before the function definition is encountered. Since functions are expressions in this case, trying to invoke it before the actual assignment results in a ReferenceError.
x behaves like a normal variable and is hoisted as undefined, so console.log(x) outputs undefined.
Function Expression
var getName2 = function() {
console.log("Sujay");
};
In this case, getName2 is also treated as a variable, and like other variables, it's hoisted as undefined. If you try to call the function before the declaration:
getName2(); // Throws TypeError: getName2 is not a function
console.log(x); // Outputs: undefined
This would result in an error because the variable getName2 is hoisted, but the assignment hasn't happened yet. Therefore, it’s not yet a function when the code tries to invoke it.
Between Function Declarations and Expressions
Function Declarations (e.g., function getName() {}) are hoisted with their entire body, making them available anywhere in the scope.
Function Expressions (e.g., var getName = function() {} or var getName = () => {}) are hoisted as variables with an initial value of undefined, so you cannot call them before the assignment.
Taking one more example to explain:
First global execution context will create.
Initially, the code starts executing in the global execution context.
The global context is the first context pushed onto the call stack when the program begins.
In JavaScript, when the code is executed, it goes through two main phases:
Memory Creation Phase (also called the "Creation Phase")
Code Execution Phase
Memory Creation Phase:
During this phase, the JavaScript engine allocates memory for variables and functions.
Variables are assigned the value undefined initially, and functions are stored with their definitions.
For example:
var x = 1;
function a() {
var x = 10;
console.log(x);
}
function b() {
var x = 100;
console.log(x);
}
var x = 1; will allocate memory for x globally with an initial value of undefined in the creation phase.
The functions a and b are stored with their definitions, but they are not executed yet.
Code Execution Phase:
In this phase, the code starts to execute line by line.
Each function call creates a new execution context (EC) where new variables are allocated space and initialized to undefined.
Breakdown of Execution:
Global Execution Context:
The global execution context is created, and memory for x is allocated.
Initially, x is set to undefined.
Function a() Invocation:
When a() is called, a new execution context is created for function a().
A new x variable is declared inside a() and initialized to undefined.
The local x inside a() is set to 10, and console.log(x) prints 10.
Function b() Invocation:
Similarly, when b() is called, another execution context is created for function b().
A new x variable is declared inside b() and initialized to undefined.
The local x inside b() is set to 100, and console.log(x) prints 100.
Back to Global Execution Context:
- After both functions have executed, the code returns to the global context, and console.log(x) prints the value of the global x, which is 1.