A closure is created when a function “remembers” the variables from its outer scope, even after that outer function has finished executing.
A closure is the combination of a function and the lexical environment within which that function was declared.
In simple terms, a closure gives you access to an outer function’s variables from an inner function, even after the outer function has returned.
function outer() {const name = 'Daniel';function inner() {console.log(`Hello, ${name}`);}return inner;}const greet = outer(); // outer() has finished, but...greet(); // Hello, Daniel ✅ still remembers `name`
While closures are powerful, they come with a few gotchas:
Closures keep variables alive even after the outer function is done. If not handled properly, this can cause memory to be held longer than necessary.
function leaky() {let bigData = new Array(1000000).fill('leak');return () => console.log(bigData.length);}
Closures can make debugging tricky, especially in deeply nested functions or asynchronous callbacks where the source of a variable isn’t obvious.
Too many nested closures can make your code hard to read and maintain — a common problem in callback-heavy code (a.k.a. callback hell).
Awesome follow-up! 🙌 Closures are super powerful and widely used in real-world applications — often behind the scenes. Here’s when and why you’d intentionally use closures in a JavaScript application:
Closures help maintain state across calls in utility functions.
function debounce(callback, delay) {let timeout;return (...args) => {clearTimeout(timeout);timeout = setTimeout(() => {callback(...args);}, delay);};}
The timeout
variable lives across function calls thanks to a closure.
Before classes and #privateFields
, closures were used for building modules with private logic:
const counterModule = (function() {let count = 0;return {inc: () => ++count,get: () => count};})();console.log(counterModule.inc()); // 1console.log(counterModule.get()); // 1
Still useful for standalone utilities or plugins.
Quick Links
Legal Stuff
Social Media