JavaScript Hoisting Explained Simply
JavaScript Hoisting Explained Simply
Comprehensive Guide
JavaScript, a versatile and widely used programming language, exhibits a behavior known as
hoisting. This refers to the process where the JavaScript interpreter appears to move the
declarations of functions and variables to the top of their current scope before the code is
executed. It is important to clarify that this is a conceptual behavior that occurs during the
compilation phase, which precedes the actual execution of the code. It's not a physical
rearrangement of the code itself. A crucial aspect of hoisting is that only the declarations are
moved to the top; any initializations or assignments remain exactly where they are written in the
code. While the term "hoisting" is commonly used among developers, the ECMAScript
specification technically defines a group of declarations as "HoistableDeclaration," which
specifically includes function declarations. The behavior is also frequently discussed in the
context of variables declared with the var keyword.
Understanding hoisting is fundamental for JavaScript developers as it directly impacts how code
behaves and can lead to unexpected outcomes and errors if not properly grasped. A lack of
comprehension can also result in code that is more difficult to read and maintain, as the order of
declarations might seem inconsistent or confusing. Interestingly, the origins of hoisting can be
traced back to the early days of the web. When JavaScript was interpreted over slower internet
connections, this mechanism helped optimize performance by processing declarations before
the main execution phase. Even though modern JavaScript engines employ compilation before
execution, hoisting remains an integral part of the language specification to ensure backward
compatibility.
2. Variable Hoisting: A Tale of Three Keywords (var, let, const)
The way JavaScript handles hoisting for variables depends on how they are declared,
specifically using the keywords var, let, and const.
Variables declared with the var keyword are hoisted to the top of their containing scope, which is
either the function in which they are declared or the global scope if declared outside any
function. When a var variable is hoisted, it is initialized with the value undefined. This means the
variable is recognized as existing within its scope even before the line of code where it is
actually declared, but it doesn't hold a meaningful value until that line is executed. Consider the
following code:
console.log(myVar); // Output: undefined
var myVar = 10;
console.log(myVar); // Output: 10
In this example, the first console.log(myVar) does not throw an error. Instead, it outputs
undefined because the declaration var myVar; is hoisted to the top of the scope and initialized
with undefined. The assignment myVar = 10; happens later during the execution of the code. It's
also crucial to remember that var has function scope. If a var variable is declared inside a
function, it is only hoisted to the top of that function. If declared outside any function, it has
global scope. This can sometimes lead to unexpected behavior, as demonstrated in this
example:
function exampleVar() {
if (true) {
var y = 20;
}
console.log(y); // Output: 20 (y is accessible outside the if block)
}
exampleVar();
Variables declared with let and const are also hoisted in JavaScript. However, a key difference
from var is that they are not initialized when they are hoisted. This leads to the concept of the
Temporal Dead Zone (TDZ). The TDZ is the period between the variable declaration being
hoisted and the point in the code where it is actually declared. If you attempt to access a let or
const variable before its declaration within its scope, a ReferenceError will be thrown because
the variable is in the TDZ. Consider these examples:
console.log(myLet); // Output: ReferenceError: Cannot access 'myLet'
before initialization
let myLet = 20;
console.log(myConst); // Output: ReferenceError: Cannot access
'myConst' before initialization
const myConst = 30;
The ReferenceError clearly indicates that while the declarations of myLet and myConst were
processed during compilation, they cannot be accessed until the interpreter reaches the line
where they are declared and potentially initialized. Another significant distinction is that let and
const have block scope, meaning they are only accessible within the block of code (defined by
curly braces {}) where they are declared. Furthermore, variables declared with const must be
initialized with a value at the time of their declaration, and their value cannot be reassigned later.
The introduction of let and const in ECMAScript 6 aimed to provide more predictable behavior
regarding hoisting compared to var, specifically by preventing access to variables before their
declaration through the TDZ.
The core of hoisting lies in the distinction between declaration and initialization. Hoisting moves
the declaration, which is the introduction of a variable's name, to the top of the scope. However,
the initialization, which is the assignment of a value to the variable, remains at the exact line of
code where it is written. This separation explains why a var declared variable accessed before
assignment is undefined, while let and const variables in the TDZ result in an error.
Feature var let const
Hoisted? Yes Yes Yes
Initial Value if Accessed undefined ReferenceError (due to ReferenceError (due to
Before Declaration Temporal Dead Zone) Temporal Dead Zone)
Scope Function or Global Block Block
Temporal Dead Zone No Yes Yes
(TDZ)
3. Function Declaration Hoisting: Ready Before You Call
Function declarations, defined using the function keyword followed by a name, exhibit a distinct
hoisting behavior. The entire function declaration, including the name and the body of the
function, is hoisted to the top of its scope, which can be either the global scope or the local
scope of a containing function. This allows you to call or invoke a function declared in this
manner from anywhere within its scope, even before the line where the function declaration
physically appears in your code. Consider this code snippet:
greet(); // Output: Hello!
function greet() {
console.log("Hello!");
}
This code executes without any issues, and "Hello!" is printed to the console. This is because
the JavaScript engine effectively moves the entire function greet() {... } declaration to the top
during the compilation phase, making it available before the greet() call. As previously
mentioned, function declarations are formally classified as "HoistableDeclaration" in the
ECMAScript specification. This behavior can be beneficial for code organization, allowing the
main flow of logic to appear earlier in the script, with the detailed function definitions following
afterward.
It is important to note the behavior of function declarations within code blocks like if statements
or loops. In non-strict mode, these declarations are often hoisted to the top of the enclosing
function or global scope, regardless of whether the block is executed. This can lead to
unexpected behavior and is generally not recommended. However, in strict mode (enabled by
"use strict";), function declarations within blocks are scoped to that block and are hoisted to the
top of the block itself. This provides more consistent and predictable scoping rules.
4. Function Expression Hoisting: The Variable is Known, But Not the Function
Function expressions are created when a function is assigned to a variable, such as var
myFunction = function() {... }; or using arrow functions like const myArrowFunc = () => {... };. The
way function expressions are handled during hoisting differs from function declarations. With
function expressions, only the variable declaration is hoisted, not the actual function
assignment. If the variable is declared using var, it will be hoisted and initialized to undefined.
Attempting to call this variable as a function before the assignment will result in a TypeError
because undefined is not a function. If the variable is declared using let or const, it will be
hoisted but remain in the Temporal Dead Zone. Trying to call it before the assignment will lead
to a ReferenceError. Consider these examples:
// var example
console.log(myFuncVar); // Output: undefined
try {
myFuncVar(); // Output: TypeError: myFuncVar is not a function
} catch (error) {
console.error(error);
}
var myFuncVar = function() {
console.log("Hello from function expression (var)!");
};
// let example
try {
// myFuncLet(); // Output: ReferenceError: Cannot access 'myFuncLet'
before initialization
} catch (error) {
console.error("Error:", error);
}
let myFuncLet = function() {
console.log("Hello from function expression (let)!");
};
// const example
try {
// myFuncConst(); // Output: ReferenceError: Cannot access
'myFuncConst' before initialization
} catch (error) {
console.error("Error:", error);
}
const myFuncConst = function() {
console.log("Hello from function expression (const)!");
};
As demonstrated, the behavior of function expressions during hoisting is quite different from that
of function declarations. For var, the variable exists but is undefined, leading to a TypeError if
called. For let and const, the variable is in the TDZ, resulting in a ReferenceError if accessed
prematurely. This applies to both named function expressions and arrow functions, which are
treated as variable assignments and follow the same hoisting rules as regular variables. The
distinction in how function declarations and expressions are hoisted is a frequent source of
confusion for those learning JavaScript, making it crucial to understand their different behaviors.
5. Key Differences: Function Declarations vs. Function Expressions
Function declarations and function expressions, while both used to define functions in
JavaScript, are handled differently by the hoisting mechanism. Function declarations are fully
hoisted, meaning both the declaration and the implementation are moved to the top of the
scope. This allows the function to be called before its definition appears in the code. In contrast,
for function expressions, only the variable declaration is hoisted. If the variable is declared with
var, it is initialized to undefined. If declared with let or const, it remains in the Temporal Dead
Zone. Consequently, a function expression cannot be called before it is assigned to the variable.
Attempting to do so will result in a TypeError if the variable was declared with var, or a
ReferenceError if declared with let or const.
Feature Function Declaration Function Expression
Hoisting of Declaration Yes (entire declaration) Yes (only the variable part)
Hoisting of Implementation Yes (function body is also No (function body is not
hoisted) hoisted)
Can be Called Before Yes No (results in TypeError if var,
Definition? ReferenceError if let/const)
Error if Called Before Definition No Yes
6. Common Errors and Unexpected Behavior Due to Hoisting
One common mistake arising from hoisting is attempting to access a variable declared with var
before it has been assigned a value. Due to hoisting, this will not throw a ReferenceError;
instead, the variable will have the value undefined. This can lead to subtle and potentially
difficult-to-debug logic errors. For instance, an if condition might evaluate to false unexpectedly,
or a calculation might produce an incorrect result because an operand is undefined.
Another frequent error is calling a function expression before the line where it is assigned to a
variable. If the variable was declared with var, this will result in a TypeError because you are
trying to invoke undefined as a function. If the variable was declared with let or const, a
ReferenceError will occur as the variable is still within the Temporal Dead Zone.
The way var allows redeclaration within the same scope can also lead to confusion due to
hoisting. While not strictly an error caused directly by hoisting in terms of runtime exceptions, it
can lead to unintentional overwriting of variable values, making the code harder to understand
and maintain.
Finally, the function scope of var, combined with hoisting, can sometimes lead to unexpected
behavior for developers who might be expecting block-level scoping. A variable declared with
var inside a block is hoisted to the top of the containing function, potentially making it accessible
outside the block where it was intended to be confined.
7. Best Practices to Avoid Hoisting Issues: Writing Cleaner, More Predictable Code
To mitigate the potential for errors and confusion caused by hoisting, several best practices
should be followed. One of the most effective strategies is to always declare variables at the top
of their respective scopes, whether it's a function or a block (for let and const). This practice
improves code readability and makes the scope of variables immediately apparent.
In modern JavaScript development, it is generally recommended to use let and const for
variable declarations instead of var whenever possible. Their block scope and the Temporal
Dead Zone help prevent many of the common issues associated with var hoisting, leading to
more predictable code behavior.
While function declarations are hoisted, it is still a good practice to declare functions before they
are called in the code. This enhances the readability and flow of the program. For function
expressions, this is essential as they cannot be invoked before their assignment.
Enabling strict mode ("use strict";) at the beginning of your JavaScript files or functions can also
help in avoiding some hoisting-related pitfalls, such as the accidental creation of global variables
when a variable is used without being declared.
Ultimately, while understanding hoisting is important, it is best to avoid writing code that relies on
its implicit behavior for its core logic. Being explicit with declarations and initializations will result
in more maintainable and understandable code.
8. Analogies for Understanding Hoisting: Making the Abstract Concrete
The concept of hoisting can sometimes be abstract, so using analogies can help make it more
concrete. Imagine a construction site where a hoist machine lifts materials to the top of the
building before the workers begin their day. In JavaScript, hoisting is similar: the engine "lifts" all
the variable and function declarations to the top of their scope before the code runs. Variables
declared with var are like empty containers lifted early, waiting for their values to be placed later.
Variables declared with let and const are also lifted but are kept in a special holding area
(Temporal Dead Zone) until they are explicitly handled at their designated spot. Function
declarations are like fully assembled units that are ready to be used as soon as they are lifted to
the top. Function expressions are like blueprints that are known about early (the variable name
is hoisted), but the actual construction (function definition) happens later.
Another useful analogy is to think of a chapter in a book that has a vocabulary list at the
beginning. This list tells you all the words that will be used in the chapter before you start
reading the sentences. Similarly, JavaScript hoisting can be seen as the interpreter scanning
your code and creating a mental "list" of all the variables and functions you've declared before it
starts executing the code line by line.
Finally, consider preparing for a party. You might decide on the guest list (declare variables and
functions) before the first guest arrives (code execution). Declaring a variable with var is like
knowing a guest is coming but not knowing what gift they'll bring (undefined). Using let or const
is like having guests on the list but not being able to interact with them until they arrive and are
properly introduced (Temporal Dead Zone). Function declarations are like having all the food
prepared and ready before the guests arrive. Function expressions are like having a recipe
(function definition) written down but not actually preparing the dish (assigning it to a variable)
until later.
9. Conclusion: Embracing the Nuances of Hoisting
JavaScript hoisting is a fundamental behavior where declarations of variables and functions are
conceptually moved to the top of their scope during the compilation phase. While function
declarations are fully hoisted, variable hoisting differs between var (initialized to undefined) and
let/const (which enter the Temporal Dead Zone). Function expressions are hoisted like
variables, with the function itself not being available until the assignment is executed.
Understanding hoisting is crucial for writing predictable and error-free JavaScript code. By
adhering to best practices such as declaring variables at the top of their scope, preferring let
and const, and declaring functions before their use, developers can avoid common pitfalls and
write cleaner, more maintainable JavaScript.
Works cited
https://imrankhani.medium.com/hoisting-in-javascript-unraveling-the-%EF%B8%8F-mysteries-a
nd-implications-1757393061c9 11. JavaScript Hoisting: What You Need to Know - Noble
Desktop,
https://www.nobledesktop.com/learn/javascript/javascript-hoisting-what-you-need-to-know 12.
Hoisting in JavaScript: Understanding the Mechanism and Best Practices,
https://blogs.ayushdev.com/hoisting-in-javascript-understanding-the-mechanism-and-best-practi
ces 13. A Guide to JavaScript Hoisting - StaticMania,
https://staticmania.com/blog/javascript-hoisting 14. HOISTING - Mehak Bahar,
https://mehakbahar.hashnode.dev/truths-about-javascript-only-experts-know 15. Understanding
JavaScript Hoisting | by Elizabeth Adegunwa - Medium,
https://medium.com/@adegunwaanu/understanding-javascript-hoisting-ef7b4488e6b2 16.
Hoisting in JavaScript · scribbles_, https://www.yash.works/hoisting-in-javascript 17. Hoisting in
JavaScript - Dana's {dev}adventures, https://danaciocan.com/hoisting-in-javascript 18. Grammar
and types - JavaScript - MDN Web Docs - Mozilla,
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types 19. A
Complete Guide to Understanding JavaScript Hoisting: Boost Your Coding Skills - newline,
https://www.newline.co/@Yousaf/a-complete-guide-to-understanding-javascript-hoisting-boost-y
our-coding-skills--34711c53 20. How Hoisting in JavaScript Can Cause Unexpected Results -
Jennifer Bland,
https://www.jenniferbland.com/how-hoisting-in-javascript-can-cause-unexpected-results/ 21. 9
Questions on Hoisting in JavaScript Answered - Codeguage,
https://www.codeguage.com/blog/javascript-hoisting-questions 22. 4 Habits to Avoid Bugs
related to Hoisting in your JavaScript Code - DEV Community,
https://dev.to/shubhsharma19/4-habits-to-avoid-bugs-related-to-hoisting-in-your-javascript-code-
4i3i 23. function - JavaScript - MDN Web Docs - Mozilla,
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function 24.
How does hoisting affect function declarations and expressions? | Quiz Interview Questions with
Solutions - GreatFrontEnd,
https://www.greatfrontend.com/questions/quiz/how-does-hoisting-affect-function-declarations-an
d-expressions 25. function expression - JavaScript - MDN Web Docs - Mozilla,
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function 26.
Function Declarations vs. Function Expressions & Hoisting | by Oğuzhan ÇELİKKAYA,
https://medium.com/@oguzhancelikkaya/function-declarations-vs-function-expressions-hoisting-
ace84399a172 27. JavaScript Hoisting Pitfalls: Unmasking the Hidden Dangers | by criss -
Medium,
https://medium.com/@therealayo1/javascript-hoisting-pitfalls-unmasking-the-hidden-dangers-f0
0c4b42be46