By the end of this lesson, you will be able to explain how a JavaScript closure keeps access to variables from the function that created it, even after that outer function has finished running.
What it is
A closure is a function bundled together with the variables from the scope where it was created. In JavaScript, when an inner function references variables from an outer function, the engine keeps those variables alive for the inner function. The outer function can return, but the inner function still remembers its surrounding scope.
Think of a closure as a backpack: the inner function carries the variables it needs from the outer function. Related terms include lexical scope, free variable, private state, and function factory.
Why it matters
- Closures let you create private variables without exposing them to the global scope.
- They are used in event handlers, timers, callbacks, and module patterns.
- They help build reusable functions that remember configuration or state.
- They are the foundation for many JavaScript patterns, including currying and memoization.
Syntax or steps
- Declare an outer function.
- Create a variable in the outer function's local scope.
- Return an inner function that uses that variable.
- Call the outer function once and store the returned inner function.
- Call the inner function later; it still has access to the outer variable.
Example
function makeCounter() {
let count = 0;
function increment() {
count += 1;
return count;
}
return increment;
}
const counter = makeCounter();
console.log(counter()); // 1
console.log(counter()); // 2
console.log(counter()); // 3
Part by part:
makeCounter()creates a local variablecount.increment()is the inner function. It reads and changescount.return increment;gives back the inner function, not the value ofcount.const counter = makeCounter();stores the inner function. The outer function has finished, butcountremains alive inside the closure.- Each call to
counter()updates the samecount.
Common mistakes
- Calling the outer function every time:
makeCounter()()creates a new closure each call, so the count restarts. Store the returned function instead. - Thinking closures are only for counters. They are also used for callbacks, event listeners, and private data.
- Forgetting that closures keep variables alive. If a closure references a large object, that object may remain in memory longer than expected.
- Using
varin loops with callbacks. The callback may see the final loop value. Useletor create a new closure per iteration.
When to use it
Closures are useful when you need a small function with remembered state. Compare them with classes when the state and behavior become larger.
| Approach | Best for | Example |
|---|---|---|
| Closure | One function with private state | makeCounter() |
| Class | Multiple methods and shared state | class Counter { increment() { ... } } |
| Global variable | Quick scripts only | let count = 0; |
Use a closure when you want simple private state. Use a class when you need several related methods. Avoid global variables when the state should not be changed from anywhere.
Practice
Guided exercise: write a function called makeGreeter(name) that returns a function. The returned function should accept a message and return Hello, name! message.
function makeGreeter(name) {
return function(message) {
return `Hello, ${name}! ${message}`;
};
}
const greetAlice = makeGreeter("Alice");
console.log(greetAlice("How are you?"));
Expected output: Hello, Alice! How are you?
Challenge: create makeBankAccount(startingBalance) that returns an object with deposit(amount) and getBalance(). Hint: keep balance private inside the outer function.
Quick check
Question: Why does counter() keep increasing after makeCounter() has already returned?
Answer: Because the returned function closes over count, so the variable remains accessible and alive in the closure.
Summary
Closures are functions that remember the variables from the scope where they were created. They let JavaScript keep private state alive after an outer function finishes. Use closures for small, focused functions that need remembered data.