Table of Contents
Let’s not kid ourselves—although JavaScript is the most popular and widely used programming language today, it also carries a strong wave of hate (although PHP still holds the crown, I think). And let’s be honest: some of that hate is justified. The infamous ==
operator, the handling of global variables and scope, falsy values, and the management of this
… nothing that hasn’t already been discussed a thousand times. Although, to be fair, there’s surprisingly little hate directed at how the DOM is designed—the part JS has to deal with constantly.
Getting back to my point, it’s true that many of these issues have been gradually addressed since the ES6 update, way back in 2015. But it’s also true that JavaScript has some features that are quite well-executed and worth highlighting. Among them are a few that have sparked a resurgence of JavaScript toward Functional Programming, such as Arrow Functions, closures, and currying. These not only allow for more structured and functional code, but also make it easier to use features that didn’t work so well before, like this
context, privacy (encapsulation), and code reuse.
In this article, we will dive deeper into these concepts, explaining what they are, how to use them, and why they can be helpful to you.
Closures, Currying, and Arrow Functions
What is a Closure?
A closure in JavaScript is a function that retains access to the variables from its creation context, even when it’s executed outside of that context. In other words, a function has access to its local scope, the outer scope where it was defined, and the global environment.
This concept allows functions to “remember” the state they were created with, which is especially useful when you need to keep certain values encapsulated and out of reach from other parts of the program.
Imagine you’re developing an application and you need certain functions to access specific variables without exposing them to the rest of the code. Closures allow you to encapsulate that state safely. This pattern is common in scenarios like managing user sessions, implementing role-based permissions, or controlling time-based logic such as timers.
Since we’re talking about timers, let’s take a look at how a closure can be applied to a timer with its logic fully encapsulated.
function createTimer() {
let seconds = 0;
return {
start: () => setInterval(() => seconds++, 1000),
reset: () => seconds = 0,
getTime: () => seconds
};
}
const timer = createTimer();
const timerInterval = timer.start();
setTimeout(() => {
console.log(timer.getTime()); // Output: 5 (después de 5 segundos)
timer.reset();
console.log(timer.getTime()); // Output: 0
clearInterval(timerInterval);
}, 5000);
In the example above, the createTimer
function creates a closed scope (closure) with the seconds
variable. The start
, reset
, and getTime
functions can access and modify that variable even after createTimer
has finished executing.
From the outside, there’s no direct access to seconds
, which keeps its internal state protected. This kind of pattern is very useful when you want to avoid exposing global variables or prevent other parts of the code from manipulating sensitive state.
What About Currying?
Currying is a technique from functional programming that involves transforming a function that takes multiple arguments into a sequence of functions, each taking one argument at a time. It sounds a bit abstract when you first read it (as functional programming often does), but let’s look at its benefits and go through an example to make it clearer.
Benefits
- Logic reuse: It allows you to create more specific functions from a general one, reducing code duplication.
- Simpler composition: It encourages building small functions that can be easily chained and combined.
Connection to Closures
So, what does this have to do with closures? Well, currying works precisely because of closures: each time an argument is applied, a new context is created that “remembers” the previously provided values. This way, you can build partial functions that can be completed later on.
function multiply(a) {
return function(b) {
return a * b;
};
}
const multiplyBy2 = multiply(2);
console.log(multiplyBy2(5)); // Output: 10
In this example, the multiply
function doesn’t return the result directly—it returns a new function that “remembers” the value of a
. That new function, when executed later, still has access to the original value thanks to a closure. In other words, multiplyBy2
is a specific function built from a more general one, with the first argument already “saved.”
Here’s the arrow function version as well, since we’re going to talk about them:
const multiply = a => b => a * b;
const multiplyBy2 = multiply(2);
console.log(multiplyBy2(5)); // Output: 10
The Relevance of Closures in Modern JavaScript
With the arrival of ES6, JavaScript underwent one of the most significant transformations in its history. New ways to declare variables (let, const) were introduced, along with new structures like classes, and most notably, arrow functions, which have redefined the way we write functions over the past few years.
These tools not only made the language more expressive and concise but also made working with closures in daily practice much easier. Thanks to a cleaner and more modular design, working with closures today is far more intuitive.
Arrow Functions and Closures
Arrow functions do not create their own this
context, which makes them especially useful in certain scenarios where you want to preserve the outer context. If this is your first time seeing this type of syntax (although it’s already quite adopted by many languages today), it might be hard to understand at first. However, once you get past that initial barrier, it’s clear that having more compact code helps create functions that capture and reuse values, one of the key strengths of closures.
const createAdder = (a) => (b) => a + b;
const add10 = createAdder(10);
console.log(add10(5)); // Output: 15
In this case, createAdder
generates a new function that remembers the value a
even after it has been executed. That “memory” isn’t magic: it’s a closure working behind the scenes. The arrow function syntax not only reduces the code but also makes it more readable and expressive.
Still, like anything in life, you need to know when to use them and when they might cause some headaches. Some of these issues can come from:
- Unnecessary memory accumulation: If not managed properly, closures can keep variables alive in memory longer than necessary.
- Confusion with the
this
scope: Especially when mixing traditional functions with arrow functions. Remember, arrow functions don’t have their ownthis
. - Closures inside poorly structured loops: If you create closures inside loops without care (e.g., using
var
), you might end up capturing unexpected references.
In summary, closures are one of the most elegant and powerful features of JavaScript. Understanding them thoroughly will not only help you write better code but also master advanced patterns used in modern libraries, frameworks, and functional programming.
Conclusion
JavaScript has its highs and lows, but if there’s one thing it does well, it’s offering us powerful tools, though sometimes not so obvious, that help us write better code. Closures, currying, and arrow functions aren’t just academic flourishes of functional programming: they’re real resources that can help you write cleaner, more modular code and, in many cases, code that’s easier to maintain.
Understanding the concepts of closures, currying, and arrow functions isn’t just about “knowing more JavaScript,” but about learning to think better in JavaScript. Because, at the end of the day, it’s not about memorizing techniques, but about knowing when to use them to solve real problems and make your code more expressive and easier for others to understand.
And although we’ve constantly talked about functional programming—I don’t know if I’ve mentioned it enough…—keep in mind that you can also apply these concepts to your object-oriented code. I’ve been getting a lot of mileage out of this mix lately… but that, probably, would make for another whole article.
Resources
To leave you not wanting more… here are a few resources for you to dive deeper into the concepts we’ve covered:
- MDN Web Docs – Closures: The official MDN documentation on closures and how they work in JavaScript.
- JavaScript.info – Closures: A very comprehensive and accessible article on closures, perfect for beginners.
- Eloquent JavaScript – Functional Programming: A free book that goes deep into functional programming in JavaScript, with practical examples of currying and closures.
- FreeCodeCamp – Arrow Functions: An article from FreeCodeCamp on arrow functions and how to use them in JavaScript.
- YouTube – JavaScript Closures Explained: A video that visually explains how closures work in JavaScript.
Author
-
Web developer, strongly oriented to the front end but with extensive backend knowledge that allows him to develop a better teamwork. His main motivation is continuous learning
View all posts