Closure in Javascript

Closure in Javascript

Closure Scope Chain

Closure scope chain refers to the hierarchy of nested functions and their respective scopes that closures have access to. The sum function creates nested closures, allowing access to variables like a, b, c, d, and e from different levels of scope within the chain.

function sum(a) {
    return function(b) {
        return function(c) {
            return function(d) {
                return function(e) {
                    return a + b + c + d + e;
                };
            };
        };
    };
}

const innermostClosure = sum(1)(2)(3)(4);
console.log(innermostClosure(5)); // Output: 15

Output of Code Snippet

The code snippet defines an immediately invoked function expression (IIFE) that declares a global variable count and a local variable count inside the printCount function. The output will be 1 followed by 0 because of variable shadowing and the conditional check inside printCount.

let count = 0;

(function() {
    if (false) {
        var count = 1;
    }
    console.log(count);
})();

// Output: 1 (due to variable shadowing) followed by 0

Function Similar to addSix() using Closures

The createBase function returns a closure that adds a base number (6 in this case) to an inner number provided as an argument. The addSix function created using createBase(6) adds 6 to its argument when invoked.

function createBase(base) {
    return function(num) {
        return base + num;
    };
}

const addSix = createBase(6);
console.log(addSix(10)); // Output: 16

Closures for Time Optimization

The find function precomputes an array of squares and returns a closure that can access this precomputed data. This approach optimizes time by avoiding redundant computations, as demonstrated by the timed execution of closure(6) and closure(50).

function find() {
    const squares = [];
    for (let i = 0; i <= 10000; i++) {
        squares.push(i * i);
    }

    return function(x) {
        return squares[x];
    };
}

const closure = find();
console.time('Closure(6)');
console.log(closure(6)); // Output: 36
console.timeEnd('Closure(6)');

console.time('Closure(50)');
console.log(closure(50)); // Output: 2500
console.timeEnd('Closure(50)');

Private Counter Using Closure

The counter function utilizes closure to create a private variable _counter that can only be accessed and modified through the returned functions add and retrieve, ensuring data privacy and controlled access.

function counter() {
    let _counter = 0;

    return {
        add: function() {
            _counter++;
        },
        retrieve: function() {
            return _counter;
        }
    };
}

const myCounter = counter();
myCounter.add();
myCounter.add();
console.log(myCounter.retrieve()); // Output: 2

Module Pattern Example

The Module Pattern uses an immediately invoked function expression (IIFE) to encapsulate private variables and functions, exposing only a public interface. In the given example, the module object has a public method publicMethod, while privateMethod remains hidden within the module.

const module = (function() {
    const privateMethod = function() {
        console.log('This is a private method.');
    };

    return {
        publicMethod: function() {
            console.log('This is a public method.');
            privateMethod();
        }
    };
})();

module.publicMethod(); // Output: "This is a public method." followed by "This is a private method."

Function Runs Only Once Using Closure

The Like function returns a closure that tracks the number of times it has been called, ensuring its action (like subscribing) is performed only once. Subsequent calls to the closure display a message indicating that the action has already been performed.

function Like() {
    let liked = false;

    return function() {
        if (!liked) {
            console.log('Liked!');
            liked = true;
        } else {
            console.log('Already liked!');
        }
    };
}

const likeAction = Like();
likeAction(); // Output: "Liked!"
likeAction(); // Output: "Already liked!"

Once Polyfill Using Closure

The once function creates a closure that allows a given function to be executed only once. Upon the first call, the original function runs, and subsequent calls return the result of the initial execution, demonstrating how closure can control function execution.

function once(func) {
    let executed = false;
    let result;

    return function(...args) {
        if (!executed) {
            result = func.apply(this, args);
            executed = true;
        }
        return result;
    };
}

const printMessageOnce = once(function(message) {
    console.log(message);
});

printMessageOnce('Hello'); // Output: "Hello"
printMessageOnce('World'); // No output, function already executed

Memoize Polyfill Using Closure

The myMemoize function is a memoization polyfill that caches function results based on arguments, using closure to store and retrieve cached values efficiently. This improves performance by avoiding redundant computations for repeated function calls with the same arguments.

function myMemoize(func) {
    const cache = {};

    return function(...args) {
        const key = JSON.stringify(args);
        if (!cache[key]) {
            cache[key] = func.apply(this, args);
        }
        return cache[key];
    };
}

function fibonacci(n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

const memoizedFibonacci = myMemoize(fibonacci);
console.log(memoizedFibonacci(10)); // Output: 55 (computed once and cached)
console.log(memoizedFibonacci(10)); // Output: 55 (retrieved from cache)