Question
How JavaScript Closures Work: A Beginner-Friendly Guide
Question
How can JavaScript closures be explained to someone who already understands functions, variables, and related basics, but does not yet understand what closures are?
I have seen the Scheme example on Wikipedia, but it did not make the idea clear for me.
Short Answer
By the end of this page, you will understand what a JavaScript closure is, why it happens automatically, how inner functions remember variables from their outer scope, and where closures are useful in real code such as event handlers, factories, callbacks, and data privacy patterns.
Concept
A closure happens when a function remembers variables from the place where it was created, even after that outer code has finished running.
In JavaScript, functions do not only carry their code. They also keep access to the lexical scope in which they were defined. That means an inner function can still use variables from an outer function later.
Here is the key idea:
function outer() {
let count = 0;
function inner() {
count++;
return count;
}
return inner;
}
const counter = outer();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
Why does this work?
outer()runs and createscountinner()is defined insideouter()outer()returnsinner- Even after
outer()finishes, still remembers
Mental Model
Think of a closure like a backpack that a function carries around.
- The function is the person
- The outer variables are items placed into the backpack
- When the function goes elsewhere, it still has access to those items
Example:
function makeGreeter(name) {
return function () {
console.log(`Hello, ${name}!`);
};
}
const greetAlice = makeGreeter("Alice");
greetAlice(); // Hello, Alice!
Even though makeGreeter has already finished, the returned function still has name in its backpack.
Another useful way to think about it:
- A variable is not copied into the function as plain text
- The function keeps access to the actual variable from its original scope
- If that variable changes, the closure sees the updated value
Syntax and Examples
Basic syntax
A closure usually appears when:
- A function is created inside another function
- The inner function uses a variable from the outer function
- The inner function is used later
function outer() {
let message = "Hello";
function inner() {
console.log(message);
}
return inner;
}
const fn = outer();
fn(); // Hello
Example: private counter
function createCounter() {
let count = 0;
return function () {
count += 1;
return count;
};
}
const counterA = createCounter();
const counterB = createCounter();
console.log(counterA()); // 1
.(());
.(());
Step by Step Execution
Consider this example:
function makeCounter() {
let count = 0;
return function () {
count = count + 1;
return count;
};
}
const counter = makeCounter();
console.log(counter());
console.log(counter());
Step-by-step
1. makeCounter is defined
At this point, nothing runs yet. JavaScript only stores the function definition.
2. makeCounter() is called
const counter = makeCounter();
Inside this call:
- A new local variable
countis created with value0 - A new inner function is created
- That inner function uses
count
Real World Use Cases
Closures are common in real JavaScript programs.
1. Event handlers
function setupButton(message) {
return function () {
console.log(message);
};
}
const handleClick = setupButton("Button clicked");
The event handler remembers the message it was created with.
2. Timers and callbacks
function delayedGreeting(name) {
setTimeout(function () {
console.log(`Hello, ${name}`);
}, 1000);
}
The callback remembers name even though it runs later.
3. Data privacy
function createBankAccount() {
let balance = 0;
return {
() {
balance += amount;
},
() {
balance;
}
};
}
Real Codebase Usage
In real projects, closures are often used as small building blocks rather than as isolated examples.
Common patterns
Guarded configuration
function createLogger(level) {
return function log(message) {
if (!message) return;
console.log(`[${level}] ${message}`);
};
}
The returned logger remembers its configured level.
Validation helpers
function minLength(length) {
return function (value) {
return value.length >= length;
};
}
const isLongEnough = minLength(8);
This pattern is common in form validation.
Array methods with closures
() {
() {
value > limit;
};
}
numbers = [, , , ];
result = numbers.(());
.(result);
Common Mistakes
1. Thinking closures copy values once
Closures keep access to variables, not just a frozen copy.
let value = 1;
function showValue() {
console.log(value);
}
value = 2;
showValue(); // 2
The function sees the current value of value.
2. Confusing separate closures with shared state
function makeCounter() {
let count = 0;
return function () {
count++;
return count;
};
}
const a = makeCounter();
const b = makeCounter();
a and b do not share the same count.
3. Using var in loops incorrectly
Comparisons
Closures vs related ideas
| Concept | What it means | How it differs from a closure |
|---|---|---|
| Regular function call | A function runs and uses available variables | A closure specifically remembers outer variables after creation |
| Global variable | A variable accessible from many places | A closure can keep data private instead of global |
| Object property | State stored on an object | A closure stores state in lexical scope, not on a public object |
| Class private field | Private data inside a class | Closures provide privacy without requiring classes |
| Parameter | Input passed into a function call | A closure can remember values without passing them every time |
Closure vs object for state
function createCounterClosure() {
let count = ;
() {
++count;
};
}
counter = {
: ,
() {
++.;
}
};
Cheat Sheet
Closure quick reference
Definition
A closure is a function that remembers variables from its outer lexical scope.
Common pattern
function outer() {
let value = 10;
return function inner() {
return value;
};
}
Key rules
- Functions can access variables from outer scopes
- Returned inner functions can keep using those variables later
- Each call to the outer function usually creates a new closure
- Closures remember variables, not just initial values
- Closures are created automatically in JavaScript
Useful for
- Private state
- Counters
- Event handlers
- Callbacks
- Function factories
- Validation helpers
Loop warning
Prefer let over var in loops when closures are involved.
Mental shortcut
A closure is a function plus its remembered scope.
FAQ
What is a closure in JavaScript?
A closure is a function that keeps access to variables from the scope where it was created, even after that outer scope has finished.
Why are closures useful?
They help preserve state, create private data, configure reusable functions, and make callbacks more powerful.
Are closures created manually?
No. JavaScript creates them automatically whenever a function uses variables from an outer scope.
Do closures copy variable values?
Usually no. They keep access to the variable itself, so they can see updated values.
Are closures only used with returned functions?
No. They also appear in callbacks, event handlers, timers, and methods defined inside other functions.
Can closures cause bugs?
Yes. A common issue is using var in loops, causing multiple functions to share the same variable.
Do closures affect memory?
They can keep referenced data alive longer, so avoid capturing large unnecessary objects.
Are closures the same as private variables?
Not exactly, but closures are one common way to create private state in JavaScript.
Mini Project
Description
Build a small counter factory that demonstrates private state with closures. This project shows how a function can create and return methods that remember internal variables without exposing them directly. It is a practical example of how closures are used to manage state safely.
Goal
Create a counter generator that can increment, decrement, and read a private count value using closures.
Requirements
Create a function that initializes a private count value.
Return an object with increment, decrement, and getValue methods.
Ensure the count value cannot be changed directly from outside the returned object.
Create two independent counters and show that they do not share state.
Keep learning
Related questions
Deep Cloning Objects in JavaScript: Methods, Trade-offs, and Best Practices
Learn how to deep clone objects in JavaScript, compare structuredClone, JSON methods, and recursive approaches with examples.
Get Screen, Page, and Browser Window Size in JavaScript
Learn how to get screen size, viewport size, page size, and scroll position in JavaScript across major browsers with clear examples.
How to Change an Element's Class with JavaScript
Learn how to change, add, remove, and toggle an HTML element's class with JavaScript using events like click.