Hoisting in JavaScript is a simple idea, but it is really important to understand it. Hoisting happens for variables and functions both.
DEFINITION
Hoisting refers to the internal process of JavaScript, where the declaration of variables, and functions are moved to the top of the scope.
WARNING
Hoisting happens only for the variables defined with “var“, and functions defined using function declaration.
For the variables defined with “let” or constants defined with “const” or for a class defined with “class” keyword, hoisting will not happen.
Let’s check the details of hoisting-
Variable Hoisting
Variable hoisting means, the declaration of the variable to the top of the scope, which is done internally by JavaScript. Check the following cases-
Case #1: Variable in the Global Scope
- In the global scope, we have defined a variable named “someExampleData”.
- We are trying to log the variable “someExampleData” (using consoel.log) before the line where it is defined.
console.log(someExampleData);
var someExampleData = 22;
NOTES
- Here we are trying to log the variable even before defining it.
- At first glance, we might think this will throw an error, because the variable is used even before defining it.
Output:
undefined
Even with strict mode enabled, this will not throw any exception.
What is really going on here?
Internally the declaration part is hoisted to the top of the script. And the value setting/assigning is done on the line where the variable is defined.
So, when we log someExampleData, the variable is available as it is already declared at the top. And the log will be “undefined”, as the variable is only declared, but not the value is defined.
So, it becomes like below-
var someExampleData; // Declaration moved to the top
console.log(someExampleData);
someExampleData = 22; // Value assigment
We say that the variable(declaration) is hoisted to the top of the script.
Case #2: Variable in Function Scope
- In the function, we have defined a variable named “someExampleData”.
- We are logging the variable “someExampleData” before definition.
function bigBoxFunc() {
console.log(someExampleData);
var someExampleData = 22;
}
bigBoxFunc();
Output:
undefined
What is really going on here?
If a variable is defined inside a function, then the declaration is hoisted to the top of the function. But the value assignment stays where it is.
We can think of the above code as-
function bigBoxFunc() {
// Declaration of the variable is hosited to the top
var someExampleData;
console.log(someExampleData);
someExampleData = 22;
}
bigBoxFunc();
Function Hoisting
The full function definition is hoisted to the top of the scope. Check the examples below-
Case #1: Normal Function
- We have defined a function named “bigBoxFunc”.
- We are trying to use the function(calling the function) before the definition.
bigBoxFunc();
function bigBoxFunc() {
console.log("bigBoxFunc executed");
}
NOTES
- We might think that the above code will throw some error, as when the function is called, it is not defined at that point.
Output:
bigBoxFunc executed
What is going on here?
Internally JavaScript is moving the function definition to the top of the scope. So whenever the function is used, it will not give any error or exception.
Think of it as below-
// Function definition is moved to the top of the scope.
function bigBoxFunc() {
console.log("bigBoxFunc executed");
}
bigBoxFunc();
// The funtion definition is moved from
// here to the top of the scope
// function bigBoxFunc() {
// console.log("bigBoxFunc executed");
// }
Case #2: Nested Function
- We have defined a function named “bigBoxFunc”.
- Inside “bigBoxFunc” we have defined a function named “insideBigBox”.
- We are calling the “insideBigBox” function before the function definition.
// Call function
bigBoxFunc();
function bigBoxFunc() {
console.log("bigBoxFunc executed");
// Call the inner function before defination
insideBigBox();
// Define inner function
function insideBigBox() {
console.log("insideBigBox executed");
}
}
Output:
bigBoxFunc executed
insideBigBox executed
What is going on here?
The definition of “insideBigBox” function is moved to the top of the scope, it is in. As it is in the “bigBoxFunc” function, so the “insideBigBox” function definition is moved to the top of the “bigBoxFunc“.
Think of it as below-
// Call function
bigBoxFunc();
function bigBoxFunc() {
// Define moved here
function insideBigBox() {
console.log("insideBigBox executed");
}
console.log("bigBoxFunc executed");
// Call the inner function
insideBigBox();
}
Case #3: [Error] Anonymous Function Assigned to a Variable
- We have defined a anonymous function and assigned it to a variable named “bigBoxFunc”.
- We are trying to call the function before the definition.
bigBoxFunc();
var bigBoxFunc = function () {
console.log("bigBoxFunc executed");
}
Output:
Uncaught TypeError: bigBoxFunc is not a function
What is happening here?
We are getting an error. Because the anonymous function is assigned to a variable.
Though the variable is hosited to the top of the scope, but at the top the variable is “undefined”. So when the function is called, it actually called “undefined()“.
This gives an error, as “undefined” is not a function.
Think of it like below-
// Variable is declare at the top
var bigBoxFunc;
// Here bigBoxFunc is "undefined" and we can not call "undefined()"
bigBoxFunc(); // During call "bigBoxFunc" does not have any value
// Value of the variable is assigned here
bigBoxFunc = function () {
console.log("bigBoxFunc executed");
}
// If we call the function here, then it will give use proper output,
// as bigBoxFunc has the function assigned to it
Temporal Dead Zone(TDZ)
As we know hoisting does not happen for the following-
- Variable defined with “let“.
- Constant defined with “const“.
- Class defined using the “class” keyword.
This introduces us to a new idea, which is named “Temporal Dead Zone” or “TDZ” in short. This TDZ happens for the 3 cases mentioned above.
The Temporal Dead Zone starts at the beginning of the scope(block), and stops where the property(variable, constant, or class) is defined.
This is called a dead zone, as the properties(variable, constant, class) are not accessible in this zone. If we try to access those, then it will throw an error.
Let’s check at a few examples-
Example #1: TDZ for One Variable
- If the variable declared with let, a constant, or a class is in a global scope, then the TDZ starts at the beginning of the script.
- TDZ for the property stops when the property(with let, const, class) is defined.
// TDZ starts for bigBoxWeight
console.log(bigBoxWeight); // This will throw error as we are trying to access bigBoxWeight in TDZ
let bigBoxWeight = 100; // TDZ ends for bigBoxWeight
console.log(bigBoxWeight); // safe place to use bigBoxWeight
Example #2: TDZ for Separate Variables
- If 2 separate properties(variable/const/class) are in the same scope then the TDZ for both starts at the beginning of the scope.
- But the TDZ of those 2 properties ends separately. Each ends when the property is defined.
// TDZ starts for bigBoxWeight
// TDZ start for bigBoxConst
console.log(bigBoxWeight); // This will throw error as we are trying to access bigBoxWeight in TDZ
console.log(bigBoxConst); // Throws error as we are accessing bigBoxConst in TDZ
let bigBoxWeight = 100; // TDZ ends for bigBoxWeight
console.log(bigBoxWeight); // Safe place to use bigBoxWeight
console.log(bigBoxConst); // Throws error as we are accessing bigBoxConst in TDZ
const bigBoxConst = 789; // TDZ start for bigBoxConst
console.log(bigBoxConst); // Safe place to use bigBoxConst
Example #3: TDZ in a Block
- If a property(variable/const/class) is in a certain block, then the scope of the property starts at the beginning of the block.
- So the TDZ will start at the beginning of the block and end when the property is defined.
var someValue = 123;
{
// TDZ for valueInBlock starts here
function bigBoxFunc() {
console.log(valueInBlock); // Using the poperty is fine, as this is not immediately used
}
bigBoxFunc(); // throws error, as the function has valueInBlock, and the function is called in the TDZ of valueInBlock
const valueInBlock = 654; // TDZ for valueInBlock ends here
bigBoxFunc(); // Safe to use this here.
}
console.log(someValue);