Closures are a fundamental yet powerful feature of JavaScript. They are often one of the most misunderstood concepts, specially among beginners. If you are looking to deepen your understanding of closures and learn how to use them in real world situations this article is for you.
What Are Closures?
In JavaScript a closure is a unique function that remembers the environment in which it was created. What it means is that it can access variables from its outer scope even after the outer function has finished the execution.
Here is a simple example to illustrate the concept of closures:
function greet() {
const name = 'Alex';
function displayName() {
alert('Hello, ' + name);
}
return displayName;
}
const myGreet = greet();
myGreet(); // Alerts "Hello, Alex"
In the code shown above displayName
is a closure that encloses both the function itself and the environment in which it was created which includes the variable name
.
Why Use Closures?
Closures are useful for many reasons:
- Closures allow you to emulate private variables which provides security and prevents the global namespace from being cluttered.
- Closures enable powerful functional programming techniques.
- Closures maintain state in an asynchronous javascript world.
Here are some Practical Use Cases:
- Data Encapsulation and Privacy:
function createCounter() {
let count = 0;
return {
increment() { count += 1; },
get value() { return count; }
};
}
const counter = createCounter();
counter.increment();
console.log(counter.value); // 1
The count
variable in above code is private. It is not accessible outside the scope of createCounter
other than through the closure's methods.
- Event Handlers:
function setupButton(buttonId) {
const button = document.getElementById(buttonId);
button.onclick = function() {
alert('Button ' + buttonId + ' clicked');
};
}
setupButton('myButton');
Each button click handler created by setupButton
in above code is a closure with access to its very own buttonId
variable.
- Partial Applications or Currying:
function multiply(a, b) {
return a * b;
}
function createMultiplier(multiplier) {
return function(x) {
return multiply(x, multiplier);
};
}
const double = createMultiplier(2);
console.log(double(3)); // 6
createMultiplier
returns a closure that multiplies its argument by a fixed multiplier.
Understanding the Nuances:
Closures come with their own set of potential pitfalls such as the loop problem:
for (var myi = 1; myi <= 5; myi++) {
setTimeout(function() {
console.log('myi: ' + myi);
}, myi * 2000);
}
// Outputs "myi: 6" five times
You might expect the above code to log the numbers 1 to 5 after every two seconds gap. However it will actually print the number 6 five times two seconds apart instead. This happens because the setTimeout functions are executed after the loop has completed execution at which point the variable โmyiโ has a value of 6.
To fix this using closures you can create a new scope by adding an Immediately Invoked Function Expression(IIFE) like below
for (var myi = 1; myi <= 5; myi++) {
(function(myj) {
setTimeout(function() {
console.log('myj: ' + myj);
}, myj * 1000);
})(myi);
}
We will not go deep into the above solution as we have a better solution for this problem in the next section. For now the quick explanation is -
myj is a parameter of the IIFE and myi is passed to it. As function parameters are local to the function this myj variable is different for each iteration which implies that setTimeout callback refers to the correct myj value when itโs called.
The modern day solution to this particular issue is to simply use let instead of var. As we already know that let is block-scoped and creates a new binding for each iteration
for (let myi = 1; myi <= 5; myi++) {
setTimeout(function() {
console.log(myi);
}, myi * 1000);
}
With the help of let each loop iteration now creates a new myi variable scoped to that iteration. That means the setTimeout callback will work with the myi that is available at the time of the iteration and gives you the expected output.
Closures should not be seen as just a theoretical concept they are a practical tool that can solve real-world problems in JavaScript world. By understanding and applying closures correctly, you can write more efficient and extremely secure code. As you dive into open-source projects like OpenSign, you'll encounter closures frequently. Understanding them will help you contribute more effectively. Visit OpenSign on GitHub to start your open source journey.
Now that you have a better grasp of closures, try implementing them in your next project or while contributing to an open-source initiative. Share your experiences or any questions in the comments below!
Top comments (3)
Unfortunately this is not correct, for two reasons:
Misconceptions About Closures
Jon Randy ๐๏ธ ใป Sep 27
Nice article on closures. Always useful in JavaScript when you need to reference something outside of normal scope.
Great Share