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.