Closures in JavaScript: Understand Them Thoroughly to Code Like a Pro
Closures—also known as "closed functions"—are one of the most powerful yet confusing concepts in JavaScript. Mastering closures not only helps you ace Senior-level interviews, but also changes how you architect your entire codebase: cleaner, safer, and less buggy. Many people confuse Scope and Closure, but they’re closely related and complementary. Let’s start from the foundations.

Trung Vũ Hoàng
Author
1. What Is a Closure?
At its core, a closure is the combination of a function and the lexical environment where that function was defined. In JavaScript, every function is a closure because it remembers the scope where it was created—even after the outer function has finished executing.
Picture this: you have a special box. When you take an item out and close the lid, the item still “remembers” the environment inside the box—as if it carries a piece of the box with it. Closures work exactly like that.
The foundation for this is Lexical Scoping: JavaScript determines a variable’s scope based on where it appears in the source code at write time, not at runtime.
2. How It Works Under the Hood
When a function is called, JavaScript creates an Execution Context to store local variables. Normally, when the function finishes, this context is cleaned up by the Garbage Collector.
But if there’s an inner function that still references variables from the outer function, JavaScript keeps that environment in memory. The inner function “closes over” the variables around it, preventing them from disappearing. That’s why it’s called a closure.
Warning: Overusing closures without understanding memory cleanup can cause memory leaks—subtle bugs where your app consumes more and more RAM without obvious errors.
3. Examples from Basic to Advanced
The easiest way to “see” a closure is with a simple counter:
function createCounter() {
let count = 0; // This variable belongs to the parent function's scope
return function() {
count++; // The inner function accesses and mutates the parent's variable
return count;
};
}
const counter = createCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3The count variable can’t be accessed directly from the outside—it’s “locked” inside the closure. But the returned function can still read and modify it. Each call to counter() continues from the previous value—this is persistent state without needing a class.
One interesting point: if you create counter2 = createCounter(), it will have a completely independent memory space from counter. Each closure owns its own copy of the data.
4. Real-World Use in Professional Code
Closures aren’t academic—they appear everywhere in React, Vue, and common design patterns:
Create Private Variables
Traditional JavaScript doesn’t have a private keyword. Closures are the most natural way to create private variables—hiding internal state while exposing only the necessary methods.
Module Pattern
Wrap your logic in an IIFE and return a public API. This helps avoid polluting the global namespace—crucial in projects with multiple developers.
Currying
Transform a function that takes multiple parameters into a chain of functions that take one parameter each, with each function remembering prior arguments via closures.
Function Factories
Create functions with the same structure but different initial data—such as building event handlers for many buttons at once.
Handling Asynchrony
Capture the correct variable value in loops when working with setTimeout, Promises, or delayed API calls.
5. Pros and Cons
Pros
Great data encapsulation—protects internal state.
Reduces global variables, preventing logic conflicts.
Maintains state across calls without needing classes.
Cons
Higher memory usage—variables aren’t freed as soon as the function ends.
Nesting many closures can make code hard to debug.
May impact performance if you create thousands of closures in tight loops.
Memory optimization tip: When you no longer need a closure, set the variable holding it to null so the Garbage Collector can reclaim the memory.
6. Classic Mistakes to Avoid
A mistake almost every JS beginner makes: using a closure in a for loop with var:
// Wrong result — prints 4, 4, 4 instead of 1, 2, 3
for (var i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i);
}, 1000);
}
// Correct result — let creates a separate block scope for each iteration
for (let i = 1; i <= 3; i++) {
setTimeout(function() {
console.log(i); // Prints 1, 2, 3
}, 1000);
}The reason: var is hoisted to function scope—all callbacks share the same i. By the time the timeouts run, the loop has finished and i = 4. The let keyword creates a new block scope per iteration, which fixes this cleanly.
Summary
Closures underpin many critical patterns in modern JavaScript—from React Hooks (even useState relies on closures) to the Module Pattern, Currying, and Memoization. Understanding closures deeply helps you:
Write more secure, well-encapsulated code.
Avoid subtle bugs related to scope and memory.
Read and understand the source code of popular libraries.
Next steps to reinforce your knowledge: explore Hoisting, the Event Loop, and how JavaScript manages memory via the Call Stack and Heap—all closely related to closures.
Bài viết liên quan

Hoisting in JavaScript: The Hidden Mechanism Every Developer Must Understand
Ever seen JavaScript call a function at the very top while its definition sits at the bottom—yet it still runs? Or accessed a variable before declaration and got undefined instead of an error? That’s hoisting. Understanding it not only helps with technical interviews but is also key to how the JavaScript engine works under the hood—before any line of code executes.

Closures in JavaScript: Understand Them Thoroughly to Code Like a Pro
Closures—also known as "closed functions"—are one of the most powerful yet confusing concepts in JavaScript. Mastering closures not only helps you ace Senior-level interviews, but also changes how you architect your entire codebase: cleaner, safer, and less buggy. Many people confuse Scope and Closure, but they’re closely related and complementary. Let’s start from the foundations.

useEffect vs useMemo: 5 Key Differences You Need to Understand
Confusing useEffect and useMemo is one of the most common sources of performance issues in React apps. Think of useMemo as a calculator that remembers results, and useEffect as a switchboard operator waiting for external signals before acting.