Closures in JavaScript

 Closure is a function along with its lexical environment forms a closure that is called closure.

What is lexical Environment?

    In Simple language lexical scope  is a variable defined outside your scope or upper scope is automatically available inside your scope which means you don't need to pass it here.

let str = "javaScript";
 myFun = () => {
    console.log(str);
}

myFun();

What happen line 4. so inside c() it forms a closure with the a() lexical scope. c() function is bind to the variable of b that means if forms a closure and it has access to parent lexical scope.
function x() {
    var a = 10;
    function y() {
        console.log(a);
    }
    return y;
}
var z = x();
console.log(z);
z();
consider this example x() it return function y so what will print in console, yeah it print function y like

function y(){
    console.log(a);
}
and what will be return after calling z()?
it return 7 but why?
because this is not return only y function, it return whole closure. the closure enclosed function along with its lexical environment that was return and it was put inside z so that's why it return 7.  

function along with its lexical bundle together form a closure.

for this example, function along with its scope that's is closure. 


----------------------------------------------------------------------------------------------------------------------------------------

Closures in JavaScript - The Complete Guide

Hey everyone! Welcome back to Namaste JavaScript. Today we are going to study one of the most beautiful and powerful concepts in JavaScript - Closures!

I am super excited about this topic because closures are everywhere in JavaScript. Once you understand closures, you will see them everywhere - in event handlers, callbacks, setTimeout, and more!

What we will cover:

  • What is a Closure?
  • Lexical Scope and Lexical Environment
  • Function returning a Function
  • Closure with Examples
  • Closures and Scope Chain
  • Uses of Closures
  • Famous Interview Questions

Let's Start with a Basic Example

function x() {
    var a = 7;
    function y() {
        console.log(a);
    }
    y();
}

x();
OUTPUT:
7

Simple, right? Function y() can access variable 'a' even though 'a' is not inside y(). This is because of lexical scope.

But wait - this is not closure yet. Let me show you what closure really is!

What is a Closure?

Let's look at the MDN definition:

"A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment)."

In simple words:

Closure = Function + Its Lexical Environment

When a function is returned from another function, it still remembers its lexical scope. It carries its lexical environment along with it!

The Classic Closure Example

function x() {
    var a = 7;
    function y() {
        console.log(a);
    }
    return y;  // Returning the function!
}

var z = x();
console.log(z);
OUTPUT:
function y() {
    console.log(a);
}

We stored the returned function in variable z. Now here's the interesting part...

function x() {
    var a = 7;
    function y() {
        console.log(a);
    }
    return y;
}

var z = x();

// x() is done executing!
// Its execution context is gone from call stack!
// Variable 'a' should be garbage collected, right?

z();  // What will happen?
OUTPUT:
7

MIND BLOWN! Even though x() has finished executing and its execution context is gone, z() still prints 7!

How is this possible?

When function y was returned, it was not returned alone. It was returned along with its closure - the lexical environment where it was created!

Closures Remember References, Not Values!

Very important point - closures don't remember the VALUE, they remember the REFERENCE to the variable!

function x() {
    var a = 7;
    function y() {
        console.log(a);
    }
    a = 100;  // Changed the value!
    return y;
}

var z = x();
z();
OUTPUT:
100

Not 7! The closure remembers the reference to 'a', not the value 7. When 'a' changed to 100, the closure sees 100!

Nested Closures - Multiple Levels

function x() {
    var a = 7;
    function y() {
        var b = 100;
        function z() {
            console.log(a, b);
        }
        z();
    }
    y();
}

x();
OUTPUT:
7 100

Function z() forms a closure with both y()'s scope and x()'s scope. It can access both 'a' and 'b'!

Let's See Closure in Browser DevTools

function x() {
    var a = 7;
    function y() {
        debugger;  // Put a breakpoint here!
        console.log(a);
    }
    return y;
}

var z = x();
z();

If you open DevTools and check the Scope section, you will see:

Scope:
  Local: (empty)
  Closure (x): { a: 7 }  // Here's the closure!
  Global: { ... }

Browser explicitly shows "Closure" with the variables it has enclosed!

Closure with Nested Functions

function outest() {
    var c = 20;
    function outer(b) {
        function inner() {
            console.log(a, b, c);
        }
        var a = 10;
        return inner;
    }
    return outer;
}

var close = outest()("Hello");
close();
OUTPUT:
10 "Hello" 20

Inner function has closure over:

  • outer's scope: a = 10, b = "Hello"
  • outest's scope: c = 20

Common Interview Pattern - Returning Anonymous Function

Instead of defining and returning separately, we can do it in one line:

function x() {
    var a = 7;
    return function y() {
        console.log(a);
    };
}

var z = x();
z();
OUTPUT:
7

Same thing! Function y still forms closure with variable 'a'.

Uses of Closures

Closures are used everywhere in JavaScript:

  • Module Design Pattern
  • Currying
  • Functions like once()
  • Memoization
  • Maintaining state in async world
  • setTimeout and setInterval
  • Iterators
  • Data hiding and encapsulation

Use Case 1: Data Hiding / Encapsulation

function counter() {
    var count = 0;  // Private variable!

    return {
        increment: function() {
            count++;
            console.log(count);
        },
        decrement: function() {
            count--;
            console.log(count);
        }
    };
}

var counter1 = counter();
counter1.increment();
counter1.increment();
counter1.decrement();

// count is NOT accessible from outside!
console.log(count);  // Error!
OUTPUT:
1
2
1
ReferenceError: count is not defined

Variable 'count' is private! It can only be accessed through the returned functions. This is data hiding!

Use Case 2: Function that Runs Only Once

function once(fn) {
    let called = false;
    return function() {
        if (!called) {
            called = true;
            return fn();
        }
    };
}

const hello = once(() => console.log("Hello!"));

hello();  // Hello!
hello();  // Nothing
hello();  // Nothing
OUTPUT:
Hello!
// (nothing printed for subsequent calls)

The inner function remembers 'called' variable through closure. Once it becomes true, the function never runs again!

Use Case 3: Memoization

function memoize(fn) {
    const cache = {};
    return function(n) {
        if (n in cache) {
            console.log("From cache!");
            return cache[n];
        }
        console.log("Calculating...");
        cache[n] = fn(n);
        return cache[n];
    };
}

const factorial = memoize(function(n) {
    if (n <= 1) return 1;
    return n * factorial(n - 1);
});

console.log(factorial(5));
console.log(factorial(5));
OUTPUT:
Calculating...
120
From cache!
120

The cache object is remembered through closure!

Famous Interview Question: setTimeout in Loop

This is the MOST asked closure question in interviews!

function x() {
    for (var i = 1; i <= 5; i++) {
        setTimeout(function() {
            console.log(i);
        }, i * 1000);
    }
}

x();
OUTPUT: (after 1, 2, 3, 4, 5 seconds respectively)
6
6
6
6
6

Why 6 and not 1, 2, 3, 4, 5?

Because of closure! All 5 setTimeout callbacks point to the SAME reference of 'i'. By the time they execute, the loop has finished and i = 6!

Solution 1: Use let instead of var

function x() {
    for (let i = 1; i <= 5; i++) {
        setTimeout(function() {
            console.log(i);
        }, i * 1000);
    }
}

x();
OUTPUT: (after 1, 2, 3, 4, 5 seconds respectively)
1
2
3
4
5

'let' has block scope. Each iteration creates a new 'i' in memory!

Solution 2: Use closure with IIFE

function x() {
    for (var i = 1; i <= 5; i++) {
        (function(j) {
            setTimeout(function() {
                console.log(j);
            }, j * 1000);
        })(i);
    }
}

x();
OUTPUT: (after 1, 2, 3, 4, 5 seconds respectively)
1
2
3
4
5

We create a new closure for each iteration by wrapping setTimeout in an IIFE. Each callback has its own copy of 'j'!

Disadvantages of Closures

  • Memory consumption: Closed-over variables are not garbage collected
  • Memory leaks: If not handled properly, can cause memory leaks
  • Performance: Creating closures in loops can be expensive

Quick Recap - Interview Cheat Sheet

Concept Explanation
Closure Function + Lexical Environment bundled together
Lexical Scope Inner function can access outer function's variables
Reference Closures remember references, not values
Scope Chain Nested closures form a chain of scopes

Interview Tips - How to Explain

Q: What is a Closure?

"A closure is a function bundled together with its lexical environment. When a function is returned from another function, it carries its lexical scope with it - it remembers the variables from its outer scope even after the outer function has finished executing."

Q: Why do closures exist?

"Closures exist because of lexical scoping in JavaScript. Functions remember where they were created and can access variables from that scope. This is fundamental to how JavaScript works."

Q: What are uses of closures?

"Closures are used for data hiding/encapsulation, module design pattern, currying, memoization, maintaining state in async operations, and functions like setTimeout callbacks."

Key Points to Remember

  • Closure = Function + Lexical Environment
  • Functions remember their birthplace (where they were created)
  • Closures remember references, not values
  • Nested functions create scope chain
  • Closures enable data hiding and encapsulation
  • let vs var in loops - different closure behavior
  • Closures can cause memory leaks if not careful
  • Most design patterns in JS use closures

Keep coding, keep learning!