Understanding JavaScript Closures: A Beginner's Guide
Published on June 9, 2025 • 4 min read

JavaScript closures are a fundamental concept that often confuses beginners, yet understanding them deeply can elevate your coding skills. Closures arise naturally when functions return other functions, enabling inner functions to retain access to variables from their outer scopes, even after the outer function has finished executing. This concept is a cornerstone in functional programming with JavaScript and is essential for data encapsulation, event handling, and performance optimization.
Understanding Functions as First-Class Citizens in JavaScript
In JavaScript, functions are first-class citizens, meaning they behave like objects. They can be stored in variables, passed as arguments, or even returned from other functions. Consider this example:
function funOne() {
console.log("Inside funOne function");
function funTwo() {
console.log("Inside funTwo function");
}
return funTwo;
}
const exm = funOne();
exm(); // Calls funTwo
Here, funOne
returns the nested function funTwo
. We store this returned function in exm
and invoke it, demonstrating how functions can be passed and returned as values.
This behavior is possible because functions are objects stored on the heap with references managed via variables. When funOne
is called, it creates an execution context and returns a reference to funTwo
. This is the memory model that sets the stage for understanding closures.
The Execution Context and Memory in JavaScript
JavaScript manages function execution using two key memory components: the call stack and the heap. When a program runs:
- The call stack handles the execution contexts in a last-in, first-out manner.
- The heap stores objects, including function objects.
Execution contexts have two phases:
- Creation Phase: Declarations of variables and functions are processed.
- Execution Phase: The code executes and variable assignments happen.
This is crucial for closures because even after the execution context for the outer function completes, memory associated with its variables can persist if the inner function retains access to them.
What Are Closures? Explaining with an Example
A closure is formed when an inner function retains access to variables of its outer (enclosing) function even after the outer function has finished execution.
Consider this code:
function outerFunction() {
let outerVariable = 10;
return function innerFunction() {
console.log("Outer variable's value is", outerVariable);
};
}
const exm = outerFunction();
exm(); // Outputs: Outer variable's value is 10
Even though outerFunction
has completed execution, innerFunction
still has access to outerVariable
because of the closure. This happens because the inner function forms a closure over the lexical scope — the environment where the function was declared.
This concept is what prevents variables in the outer function from being garbage collected as long as the closure exists.
Practical Use Case: Building a Counter with Closures
Closures are especially useful in scenarios requiring data encapsulation and state management. Here's an example of a counter function that maintains its own count independent of other counters:
function createCounter() {
let count = 0;
return function () {
count += 1;
console.log("Count is", count);
};
}
const counterA = createCounter();
counterA(); // Count is 1
counterA(); // Count is 2
const counterB = createCounter();
counterB(); // Count is 1
counterB(); // Count is 2
Each call to createCounter
creates a new count
variable scoped to that specific counter function because the inner function closes over its lexical scope. This example illustrates how closures allow for private variables and state to persist across function calls.
For more on function scopes and closures, check the MDN Web Docs on Closures.
Why Are Closures Important in JavaScript?
Closures are not just an academic concept but power several real-world JavaScript features:
- Data Encapsulation: Closures help implement private variables and methods, foundational in object-oriented JavaScript.
- Event Handling: In DOM manipulation and event listeners, closures enable handlers to maintain state and context.
- Memoization: Closures aid in caching results to improve performance and reduce redundant computations.
- Functional Programming: Many advanced JavaScript patterns depend on closures for composing functions and managing side effects.
Understanding closures fully prepares you to write efficient and maintainable code in JavaScript.
Mastering closures is a game-changer in JavaScript programming. They provide powerful capabilities to manage state, control access to variables, and optimize performance. Keep practicing with examples like nested functions and counters demonstrating lexical scope. If you found this explanation helpful, experiment with closures in your projects and explore the related topics on JavaScript functions and scope in official documentation. Happy coding!
This blog post is based on content from a YouTube video. Watch it here. All rights reserved by the original creator.