Preparation
πŸ’‘ ⏐ JavaScript Questions
16. Closures

Closures in Javascript

Closures are an extremely powerful property of JavaScript (and most programming languages). As defined on MDN (opens in a new tab)

Closures are functions that refer to independent (free) variables. In other words, the function defined in the closure β€˜remembers’ the environment in which it was created.

Note: Free variables are variables that are neither locally declared nor passed as parameter.

What is Lexical Scope?

Explanation: Lexical scope refers to how variable names are resolved in nested functions based on where the functions are defined. In the provided code, the function local has a local scope where it can access variables defined within itself as well as variables in the global scope, such as the username variable.

var username = "frontendzip";
 
function local() {
  console.log(username); // frontendzip
  var userId = 2;
}
 
local();

How does Closure work?

Explanation: Closure is a JavaScript feature that allows a function to remember and access its lexical scope even when the function is executed outside that scope. In the code snippet, makeFunc creates a closure around the name variable, enabling displayName to access name even after makeFunc has finished running.

function makeFunc() {
  const name = "Mozilla";
  function displayName(num) {
    console.log(name, num);
  }
  return displayName;
}
 
makeFunc()(5); // Mozilla 5

Closure Scope Chain

Explanation: 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.

const e = 10;
function sum(a) {
  return function (b) {
    return function (c) {
      return function (d) {
        return a + b + c + d + e;
      };
    };
  };
}
 
console.log(sum(1)(2)(3)(4)); // 20

Question 1: What will be the output of the provided code snippet?

Output: The output will be 1 followed by 0 because of variable shadowing and the conditional check inside printCount.

let count = 0;
(function printCount() {
  if (count === 0) {
    let count = 1;
    console.log(count); // 1 shadowing
  }
  console.log(count); // 0
})();

Question 2: Write a function similar to addSix() using closures.

Explanation: The createBase function returns a closure that adds a base number to an inner number provided as an argument.

function createBase(num) {
  return function (innerNum) {
    console.log(innerNum + num);
  };
}
var addSix = createBase(6);
addSix(10); // 16
addSix(21); // 27

Closure for Time Optimization

Explanation: The find function precomputes an array of squares and returns a closure that can access this precomputed data.

function find() {
  let a = [];
  for (let i = 0; i < 1000000; i++) {
    a[i] = i * i;
  }
  return function (index) {
    console.log(a[index]);
  };
}
const closure = find();
console.time("6");
closure(6);
console.timeEnd("6");
console.time("50");
closure(50);
console.timeEnd("50");

Question 3: Block scope and set Time out and print that value and you are not allowed to use let use var to print 0,1,2

Explanation: Using var inside the loop creates closures that capture the value of i at each iteration.

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

Question 4: How would you create a private counter using closure?

Explanation: The counter function utilizes closure to create a private variable _counter that can only be accessed and modified through the returned functions add and retrive.

function counter() {
  var _counter = 0;
 
  function add(increment) {
    _counter += increment;
  }
 
  function retrive() {
    return "Counter = " + _counter;
  }
 
  return {
    add,
    retrive,
  };
}
const c = counter();
c.add(5);
c.add(10);
console.log(c.retrive());

Question 5: Explain the Module Pattern and provide an example.

Explanation: The Module Pattern uses an immediately invoked function expression (IIFE) to encapsulate private variables and functions, exposing only a public interface.

var module = (function () {
  function privateMethod() {
    console.log("private");
  }
  return {
    publicMethod: function () {
      console.log("public");
    },
  };
})();
module.publicMethod();

Question 6: How can you ensure a function runs only once using closure?

Explanation: 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.

let view;
function Like() {
  let called = 0;
 
  return function () {
    if (called > 0) {
      console.log("Already");
    } else {
      view = "FrontedZip";
      console.log("Subscribe", view);
      called++;
    }
  };
}
let isSub = Like();
isSub();
isSub();
isSub();
isSub();

Question 7: Explain the Once Polyfill using closure.

Explanation: The once function creates a closure that allows a given function to be executed only once.

function once(func, context) {
  let ran;
 
  return function () {
    if (func) {
      ran = func.apply(context || this, arguments);
      func = null;
    }
    return ran;
  };
}
const hello = once((a, b) => {
  console.log("Hi", a, b);
});
hello(1, 2);
hello(1, 2);
hello(1, 2);
hello(1, 2);

Question 8: What is Memoize Polyfill, and how does it use closure?

Explanation: The myMemoize function is a memoization polyfill that caches function results based on arguments, using closure to store and retrieve cached values efficiently.

function myMemoize(fn, context) {
  const res = {};
  return function (...args) {
    var argsCache = JSON.stringify(args);
    if (!res[argsCache]) {
      res[argsCache] = fn.call(context || this, ...args);
    }
    return res[argsCache];
  };
}
 
const clumsyProduct = (num1, num2) => {
  for (let i = 1; i <= 100000000; i++) {
    return num1 * num2;
  }
};
 
const MemoizeClumsyProduct = myMemoize(clumsyProduct);
 
console.time("First call");
console.log(MemoizeClumsyProduct(9467, 7649));
console.timeEnd("First call");
 
console.time("Second call");
console.log(MemoizeClumsyProduct(9467, 7649));
console.timeEnd("Second call");

Question 9: Differentiate between Closure and Scope.

  • Closure: A function's ability to retain access to variables from its lexical scope.
  • Scope: Visibility and accessibility of variables within a specific context.