JavaScriptConcepts

JavaScript Closures: In-Depth Guide with Examples

The concept of closures in JavaScript, using the code examples from closures.js.


1. Basic Closure Example

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

Explanation:


2. Closure with Private Variables (Data Encapsulation)

function makeCounter() {
    let count = 0;
    return function() {
        count++;
        return count;
    };
}
const counter = makeCounter();
counter(); // 1
counter(); // 2
counter(); // 3

Explanation:


3. Closures in Loops (Common Pitfall & Solution)

Problem:

var funcs = [];
for (var i = 0; i < 3; i++) {
    funcs.push(function() {
        console.log('Problem:', i);
    });
}
funcs[0](); // 3
funcs[1](); // 3
funcs[2](); // 3

Solution 1: IIFE

var funcsFixed = [];
for (var j = 0; j < 3; j++) {
    (function(jCopy) {
        funcsFixed.push(function() {
            console.log('Fixed:', jCopy);
        });
    })(j);
}
funcsFixed[0](); // 0
funcsFixed[1](); // 1
funcsFixed[2](); // 2

Solution 2: Use let

let funcsLet = [];
for (let k = 0; k < 3; k++) {
    funcsLet.push(function() {
        console.log('Let:', k);
    });
}
funcsLet[0](); // 0
funcsLet[1](); // 1
funcsLet[2](); // 2

Explanation:


4. Closures for Function Factories

function makeMultiplier(multiplier) {
    return function(x) {
        return x * multiplier;
    };
}
const double = makeMultiplier(2);
const triple = makeMultiplier(3);
double(5); // 10
triple(5); // 15

Explanation:


5. Closures in Asynchronous Code

Problem:

for (var m = 0; m < 3; m++) {
    setTimeout(function() {
        console.log('Async Problem:', m);
    }, 100);
}
// All log 3

Solution:

for (let n = 0; n < 3; n++) {
    setTimeout(function() {
        console.log('Async Fixed:', n);
    }, 200);
}
// Logs 0, 1, 2

Explanation:


6. Practical Example: Hiding Implementation Details

function Person(name) {
    let _name = name;
    return {
        getName: function() { return _name; },
        setName: function(newName) { _name = newName; }
    };
}
const p = Person('Alice');
p.getName(); // Alice
p.setName('Bob');
p.getName(); // Bob

Explanation:


7. Interview Question: What is a closure?

A closure is a function that remembers its lexical scope even when the function is executed outside that scope. It allows functions to access variables from an enclosing scope, even after that scope has closed.

8. Interview Question: Why are closures useful?


9. Advanced: Currying with Closures

function add(a) {
    return function(b) {
        return a + b;
    };
}
const add5 = add(5);
add5(10); // 15

Explanation:


10. Memory Leaks and Closures

Be careful: Closures can cause memory leaks if they retain references to large objects unnecessarily. Always clean up references if not needed.

Example:

function createLeak() {
    let largeArray = new Array(1e6).fill('leak'); // Large object
    return function() {
        // This closure keeps largeArray in memory
        console.log('Still holding largeArray of length:', largeArray.length);
    };
}

let leaky = createLeak();
// Even if we don't need largeArray anymore, it's not garbage collected
// because the closure (leaky) still references it.
// To avoid the leak, set leaky = null when done:
// leaky = null;

Summary

Closures are a powerful feature in JavaScript, enabling data privacy, function factories, and more. Understanding closures is essential for interviews and real-world development.


setTimeout, Closures, and Loop Pitfalls

This section explains how closures interact with setTimeout and loops, a common source of confusion in JavaScript.

Example 1: The Problem with var in Loops

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

What happens?

Example 2: Fix with IIFE (Immediately Invoked Function Expression)

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

How does this work?

Example 3: Fix with let

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

How does this work?

Key Concepts

Summary:

When using asynchronous functions like setTimeout inside loops, always be mindful of variable scope. Use let or an IIFE to avoid common pitfalls with closures and var.


Practice: Try modifying the examples in closures.js & setTimeOutPractice.js and observe the results to deepen your understanding.