Advanced JavaScript: Understanding Closures with Real-World Examples

A closure is a function that remembers the variables from its lexical scope even after the outer function has finished executing.

Example 1: Using IIFE (Immediately Invoked Function Expression)

const counter = (function () {
    let count = 0;
    return function () {
        count++;
        console.log(count);
    };
})();

counter(); // Output: 1
counter(); // Output: 2
counter(); // Output: 3

Example 2: Closure in Loops (Fixing Common Mistakes)

function createTimers() {
    for (var i = 1; i <= 3; i++) {
        setTimeout(function() {
            console.log(`Timer ${i} seconds`);
        }, i * 1000);
    }
}

createTimers();

Solution Using IIFE

function createFixedTimers() {
    for (var i = 1; i <= 3; i++) {
        (function (j) { 
            setTimeout(function () {
                console.log(`Timer ${j} seconds`);
            }, j * 1000);
        })(i);
    }
}

createFixedTimers();

Alternative Fix Using let

function createFixedTimersWithLet() {
    for (let i = 1; i <= 3; i++) {
        setTimeout(function () {
            console.log(`Timer ${i} seconds`);
        }, i * 1000);
    }
}

createFixedTimersWithLet();

Example 3: Function Factories

function multiplyBy(factor) {
    return function(number) {
        return number * factor;
    };
}

const double = multiplyBy(2);
const triple = multiplyBy(3);

console.log(double(5));  // Output: 10
console.log(triple(5));  // Output: 15

Example 4: Memoization (Performance Optimization)

function memoizedFactorial() {
    let cache = {};
    return function factorial(n) {
        if (n in cache) {
            console.log("Fetching from cache:", n);
            return cache[n];
        }
        if (n === 0 || n === 1) return 1;

        console.log("Calculating result:", n);
        cache[n] = n * factorial(n - 1);
        return cache[n];
    };
}

const fact = memoizedFactorial();
console.log(fact(5));
console.log(fact(5));

Summary

  • Closures allow functions to remember variables from their outer scope.
  • Used for private variables, function factories, loops, and performance optimization.
  • Common patterns:
    • Encapsulation
    • Fixing loops with var and let
    • Function factories
    • Memoization

Comments