A closure is a function that remembers the variables from the scope where it was created — even after that scope has finished executing.You’ve already been using closures without knowing it. If you’ve written a function inside another function and used a variable from the outer function, that’s a closure.
Copy
function createGreeter(greeting) { // This inner function "closes over" the greeting variable return function(name) { return `${greeting}, ${name}!`; };}const sayHello = createGreeter("Hello");const sayHey = createGreeter("Hey");console.log(sayHello("Sarah")); // "Hello, Sarah!"console.log(sayHey("John")); // "Hey, John!"
createGreeter has finished running, but sayHello still remembers that greeting was "Hello". That’s a closure.
Closures are not “a JavaScript thing” — Python has them too. If you’ve written a nested function that uses a variable from the outer function, you’ve already used closures.
JavaScript
Python
Copy
function makeGreeter(greeting) { return function(name) { return `${greeting}, ${name}!`; };}
Closures aren’t an abstract concept you need to master — they’re a pattern that shows up naturally in JavaScript. Here are the places you’ll actually use them.
function attachHandlers(buttons) { for (let i = 0; i < buttons.length; i++) { buttons[i].addEventListener("click", () => { console.log(`Button ${i} clicked`); // Each handler remembers its own "i" }); }}
Each click handler is a closure that captures its own copy of i. This is why let (block-scoped) works correctly in loops but var (function-scoped) doesn’t.
Python mental model: this is the same family of problem as Python’s “late binding” in loops (for example, lambdas in a loop all using the final value). In JavaScript, let usually gives you the behavior you wanted automatically.
The count variable is completely private. The only way to interact with it is through the returned functions. This pattern is useful for creating modules and encapsulated state.
You use closures in React without thinking about it. Every event handler inside a component is a closure:
Copy
function SearchBar() { const [query, setQuery] = useState(""); const handleSubmit = () => { // This is a closure — it "closes over" the query variable console.log(`Searching for: ${query}`); fetch(`/api/search?q=${query}`); }; return ( <div> <input value={query} onChange={e => setQuery(e.target.value)} /> <button onClick={handleSubmit}>Search</button> </div> );}
handleSubmit remembers query from the component scope. Every time the component re-renders, a new closure is created with the latest value of query.
This is why React state “works” — your event handlers are closures that capture the current state values. When you hear about “stale closures” in React, it means a function captured an old value of state. The useEffect dependency array exists to manage this.
Closures sound complicated when explained in theory, but they’re a natural consequence of two things you already understand:
Functions can be defined inside other functions
Inner functions can access outer variables (lexical scope)
That’s it. When the inner function outlives the outer function, it keeps access to those variables. That’s a closure.
Copy
// You don't need to think "I'm creating a closure here"// Just write the code naturally:function fetchUserData(userId) { const url = `http://localhost:8000/api/users/${userId}`; // This callback is a closure — it uses "url" from the outer function return fetch(url) .then(response => response.json());}
Don’t try to memorize closure rules. Write functions inside functions, use variables from the outer scope when it’s natural, and you’re using closures. The concept will solidify through practice.
If you’re a Python web dev, you’ve likely seen closures in decorator factories (functions that return decorators). JavaScript callbacks and React event handlers use the same underlying idea constantly.
You’ve covered the core of JavaScript — variables, types, strings, functions, scope, and closures. Now let’s learn how to work with collections of data: arrays and objects.