simoong·blog

Easy Guide to JavaScript Closures

image

Easy Guide to JavaScript Closures (Closure)

JavaScript Closures are one of the most common topics in interviews and are also heavily used in real-world projects.
In this post, we’ll go step by step through what a closure is → basic examples → practical use cases → caveats → additional learning.


📌 What is a Closure?

A closure is "a combination of a function and the lexical environment in which it was declared."
In other words, a closure means that a function remembers the variables from the scope in which it was defined, and it can still access those variables even after the function has finished execution.

function outerFunction() {
    let outerVariable = 'I am outer!'
 
    function innerFunction() {
        console.log(outerVariable)
    }
 
    return innerFunction
}
 
const myClosure = outerFunction()
myClosure() // Output: I am outer!

In the example above, innerFunction is called after outerFunction has already finished execution, but it still has access to outerVariable.
👉 That’s the beauty of closures!


📌 Common Use Cases of Closures

1. Creating Private Variables (Data Encapsulation)

function createCounter() {
    let count = 0
 
    return function () {
        count++
        console.log(count)
    }
}
 
const increment = createCounter()
increment() // 1
increment() // 2
increment() // 3

Since count cannot be accessed directly from outside, the state can be safely managed.


2. Preserving State

function userSession(userId) {
    let loginCount = 0
 
    return {
        login: function () {
            loginCount++
            console.log(`${userId} login count: ${loginCount}`)
        },
        getLoginCount: function () {
            return loginCount
        },
    }
}
 
const sessionA = userSession('userA')
sessionA.login() // userA login count: 1
sessionA.login() // userA login count: 2
console.log(sessionA.getLoginCount()) // 2

👉 Perfect for features like login sessions or limiting API requests.


3. Function Factory

function greeter(name) {
    return function () {
        console.log(`Hello, ${name}!`)
    }
}
 
const greetJohn = greeter('John')
const greetJane = greeter('Jane')
 
greetJohn() // Hello, John!
greetJane() // Hello, Jane!

👉 Great for creating custom button actions or user-specific messages.


4. Implementing Currying

function multiply(a) {
    return function (b) {
        return a * b
    }
}
 
const double = multiply(2)
const triple = multiply(3)
 
console.log(double(5)) // 10
console.log(triple(5)) // 15

👉 Useful for creating calculators (e.g., discounts) or unit converters.


📌 Things to Watch Out For When Using Closures

  1. Be careful of memory leaks!
    If you keep unnecessary variables alive, you might waste memory.

  2. Possible performance issues
    Closures rely on scope chains. Using too many closures in tight loops may reduce performance.

  3. Harder to debug
    Sometimes it’s not obvious where a value was changed.
    → Use Chrome DevTools to inspect Closure snapshots.


❓ Common Closure Interview Questions

  • What is a closure in JavaScript?
  • How do you use closures to create private variables?
  • How do closures affect memory management?
  • What is the difference between closures and scope?

👉 These are very common interview questions. Preparing examples in advance will help a lot!


  • Scope: Defines the accessibility of variables.
  • Lexical Environment: The environment in which the code was written.
  • Closure: A function remembering its scope and lexical environment.
  • this: Refers to the object at execution time. It’s different from closures.

⚠️ Common Closure Mistakes (Anti-Pattern)

for (var i = 0; i < 3; i++) {
    setTimeout(function () {
        console.log(i)
    }, 1000)
}
// Output: 3, 3, 3

👉 Using var leads to unexpected results due to closures.
✅ Solution: Use let or wrap with an IIFE (Immediately Invoked Function Expression).

for (let i = 0; i < 3; i++) {
    setTimeout(function () {
        console.log(i)
    }, 1000)
}
// Output: 0, 1, 2

📌 Practical Closure Use Cases

  • Managing login sessions
  • Remembering state inside event handlers
  • Rate limiting API calls
  • Preserving state in React/Vue custom hooks
function rateLimiter(limit, interval) {
    let calls = 0
    let start = Date.now()
 
    return function () {
        const now = Date.now()
        if (now - start > interval) {
            start = now
            calls = 0
        }
 
        if (calls < limit) {
            calls++
            console.log('API call allowed!')
            return true
        } else {
            console.log('Request blocked!')
            return false
        }
    }
}
 
const limitedAPI = rateLimiter(3, 5000)
 
limitedAPI() // API call allowed!
limitedAPI() // API call allowed!
limitedAPI() // API call allowed!
limitedAPI() // Request blocked!

📚 Additional Resources


✅ Conclusion

JavaScript closures are a powerful tool for hiding data, preserving state, creating functions, and optimizing performance.
But if used excessively, they can lead to memory leaks and performance issues, so be careful.

👉 Once you truly understand closures and use them appropriately, your code will become cleaner and more robust! 🎉