JavaScript Hoisting and Types of Functions
Functions are one of the most important parts of JavaScript. They allow us to group a block of code into a reusable unit that can be executed whenever it is needed.
JavaScript provides different ways to create functions. Some common types are function declarations, function expressions, arrow functions, and IIFEs. Although all of them are functions, they behave differently in some situations, especially when hoisting is involved.
What is Hoisting in JavaScript?
Hoisting is the behavior of JavaScript where certain declarations are processed before the code is executed line by line.
For example:
console.log(name);
var name = "Abimanyu";
The output is:
undefined
This happens because the var declaration is processed before execution. Conceptually, it is similar to:
var name;
console.log(name);
name = "Abimanyu";
The important thing to notice is that only the declaration is processed early. The assignment happens at its original position.
Hoisting does not mean JavaScript physically moves the code to the top. It describes what happens during the creation phase of the execution context before the code starts executing.
The same idea applies to functions, but the behavior depends on how the function is defined.
What is a Function?
A function is a reusable block of code that performs a particular task.
function greet() {
console.log("Hello");
}
greet();
Here, greet() is a function. Once defined, it can be called whenever required.
JavaScript allows us to create functions in different ways. The way we define a function also affects how hoisting works.
1. Function Declaration
A function declaration is the traditional way of defining a function.
greet();
function greet() {
console.log("Hello");
}
This works even though greet() is called before the function declaration.
The output is:
Hello
This happens because function declarations are fully hoisted. During the creation phase, JavaScript makes the entire function available, including its function body.
So, conceptually, JavaScript already has access to:
function greet() {
console.log("Hello");
}
before it starts executing the code.
Hoisting rule
Function declarations are fully hoisted, so they can normally be called before their declaration.
This is one of the biggest differences between a function declaration and the other function types discussed below.
2. Function Expression
A function expression is a function assigned to a variable.
const greet = function() {
console.log("Hello");
};
Here, greet is not a function declaration. It is a variable that stores a function.
This distinction is important for hoisting.
For example:
greet();
const greet = function() {
console.log("Hello");
};
This results in a:
ReferenceError
The reason is that const declarations are hoisted, but they remain in the Temporal Dead Zone (TDZ) until execution reaches their declaration.
The function expression itself is also not available before that point.
If var is used instead:
greet();
var greet = function() {
console.log("Hello");
};
the result is:
TypeError: greet is not a function
This time, var greet is initialized with undefined during hoisting. The function is assigned to greet only when execution reaches the assignment.
Hoisting rule
A function expression is not hoisted as a callable function. Its behavior depends on the variable used to store it.
var → undefined before assignment
let → TDZ
const → TDZ
3. Arrow Function
An arrow function provides a shorter syntax for creating functions.
const add = (a, b) => {
return a + b;
};
console.log(add(10, 20));
Output:
30
Arrow functions are also commonly stored inside variables. Because of this, they do not receive the same function hoisting behavior as a function declaration.
For example, if an arrow function is stored in a const variable and accessed before its declaration, JavaScript produces a ReferenceError.
add(10, 20);
const add = (a, b) => {
return a + b;
};
The reason is not that arrow functions have some completely separate hoisting rule. The important point is that add is a const variable containing a function.
Therefore, the variable's rules apply:
const arrow function → TDZ → ReferenceError
let arrow function → TDZ → ReferenceError
var arrow function → undefined → TypeError
Hoisting rule
Arrow functions themselves are not hoisted as callable functions. Their behavior depends on the variable declaration used to store them.
This is why:
function greet() {}
and:
const greet = () => {};
should not be treated as identical when discussing hoisting.
4. IIFE
IIFE stands for Immediately Invoked Function Expression.
It is a function expression that is executed immediately after it is created.
(function() {
console.log("Hello");
})();
Output:
Hello
Normally, a function is defined first and called separately:
function greet() {
console.log("Hello");
}
greet();
With an IIFE, the function is executed immediately as part of the expression.
The final () invokes the function:
(function() {
console.log("Hello");
})();
IIFEs were commonly used to create a separate scope for variables and prevent them from interfering with other parts of a program.
Hoisting rule
An IIFE is a function expression, not a function declaration. Therefore, it does not receive the full hoisting behavior of a function declaration.
It executes when JavaScript reaches the expression:
(function() {
console.log("Hello");
})();
There is normally no separate function name that you can call before the IIFE.
The key idea is:
IIFE → function expression → executes when the expression is reached.
Conclusion:
The most important distinction is between a function declaration and a function stored in a variable.
// Function declaration
function greet() {}
The function itself is hoisted.
Whereas:
// Function expression
const greet = function() {};
and:
// Arrow function
const greet = () => {};
store the function in a variable, so the variable's declaration rules determine what happens before the declaration is reached.
Top comments (1)
Unfortunately not correct. Function expression and assigning a function to a variable are two different things.