Closure in JavaScript
A closure is a function that remembers its lexical scope even when it is executed outside that scope. We can use closures to create private variables. They allow functions to remember the variables from the environment where they were created, even if that environment is no longer active.
Example 1: Understanding Closure
Consider the following code:
function myFunction() {
let name = "Deepak";
function displayName() {
console.log(name);
}
show(displayName);
}
function show(displayName) {
displayName(); // "Deepak"
}
myFunction();In this code, the displayName function is declared inside myFunction, but it is executed inside the show function. Despite being executed outside its lexical scope, displayName still knows the value of the name variable. This demonstrates closure in action.
Example 2: Private Variables with Closure
Let's explore another example:
function myFunction() {
let name = "Deepak";
return function() {
console.log(name);
};
}
function show() {
myFunction()(); // "Deepak"
}
show();In this program, the function returned by myFunction is executed inside the show function, yet it retains access to the variable name. This showcases the power of closures in maintaining private variables.
Example 3: A revealing module pattern
var nameObj = (function() {
var name = "Deepak"; // private variable
return { // public API
getName: function() {
return name;
},
setName: function(newName) {
name = newName;
}
};
})();
console.log(nameObj.getName()); // "Deepak"
nameObj.setName("Piyush");
console.log(nameObj.getName()); // "Piyush"In this example, we have a closure that encapsulates a private variable name, providing public methods to access and modify its value. This pattern ensures data privacy and avoids direct manipulation of the private variable from outside the closure.
Questions
1. Write a program to implement closure
add(5)(5) // returns 10Answer
To implement closure for adding two numbers, we can use a currying pattern in JavaScript.
function add(a) {
return function(b) {
return a + b;
};
}The returned function knows the value of variable a. The program uses closure and currying to create a function add that takes an initial value a and returns a function to add another value b, resulting in the sum of a and b when called as add(5)(5).
2. Implement this program using closure
counter() // prints 1
counter() // prints 2
counter() // prints 3
counter() // prints 4 and so on....Answer
var counter = (function() {
var count = 0; // private variable
return function() { // public anonymous function
count += 1;
console.log(count);
};
})();3. What is the output of this program?
(function immediateA(a) {
return (function immediateB(b) {
console.log(a); // What is logged?
})(1);
})(0);Answer — 0
4. What is the output of this program?
let count = 0;
(function immediate() {
if (count === 0) {
let count = 1;
console.log(count); // What is logged?
}
console.log(count); // What is logged?
})();Answer — 1, 0
5. What is the output of this program?
for (var i = 0; i < 3; i++) {
setTimeout(function log() {
console.log(i); // What is logged?
}, 1000);
}Answer — 3, 3, 3
Now, let's modify the code to print 0, 1, 2 with the same syntax.
First approach
for (var i = 0; i < 3; i++) {
(function(a) {
setTimeout(function log() {
console.log(a); // What is logged?
}, 1000);
})(i);
}In the first approach, we use an immediately invoked function expression (IIFE) to capture the current value of i in a separate variable a, allowing us to print 0, 1, 2 after a delay of 1000ms.
Second approach
for (let i = 0; i < 3; i++) {
setTimeout(function log() {
console.log(i); // What is logged?
}, 1000);
}In the second approach, we use the let keyword to declare the loop variable i, which creates a new lexical scope for each iteration of the loop. As a result, the setTimeout function inside the loop can access the correct value of i for each iteration, leading to the output 0, 1, 2 after a delay of 1000ms.
6. What is the output of this program?
function createIncrement() {
let count = 0;
function increment() {
count++;
}
let message = `Count is ${count}`;
function log() {
console.log(message);
}
return [increment, log];
}
const [increment, log] = createIncrement();
increment();
increment();
increment();
log(); // What is logged?Answer — Count is 0
In the given code, the count variable is declared with the initial value of 0 using let count = 0;. When the increment function is called three times using increment();, it increments the count variable by 1 on each call.
However, the message variable is set to `Count is ${count}` only once during the creation of the closure, and it captures the value of count at that time, which is 0. As a result, when the log function is called using log();, it logs "Count is 0" to the console.