Closures: Mastering JavaScript

Understanding Closures in JavaScript

Patrick Karsh
3 min readOct 12, 2023

JavaScript, with its functional programming capabilities and asynchronous nature, possesses a powerful concept known as ‘closures.’ While the name might seem intimidating to beginners, closures are a core feature that every aspiring JavaScript developer should master. In this article, we’ll demystify the concept of closures, illustrating their utility and importance with practical examples.

What is a Closure?

In its essence, a closure is a function bundled with references to its surrounding state, ensuring that it can access the environment in which it was created. This means that a function can “remember” the variables present at its birthplace even if it’s called outside that context.

The Nitty-Gritty of Closures

To understand closures deeply, let’s dive into a simple example:

function outerFunction() {
let outerVariable = 'Hello from the outer world!';

function innerFunction() {
console.log(outerVariable);
}

return innerFunction;
}

const greet = outerFunction();
greet(); // Logs: "Hello from the outer world!"

In the example above, outerFunction defines a local variable outerVariable and an innerFunction. When we invoke outerFunction, it returns innerFunction. The fascinating part is, when we later call the greet function, it still remembers the outerVariable from the outerFunction. This "memory" is a closure in action!

Benefits of Closures

Data Encapsulation & Private Variables

In many programming languages, the concept of ‘public’ and ‘private’ variables exists, allowing developers to restrict direct access to specific variables. JavaScript doesn’t inherently provide this feature. However, closures offer a unique way to emulate this.

function createBankAccount(initialBalance) {
let balance = initialBalance; // This variable is now private

return {
deposit: function(amount) {
balance += amount;
console.log(`Deposited: ${amount}. New Balance: ${balance}`);
},
withdraw: function(amount) {
if (amount > balance) {
console.log('Insufficient funds');
return;
}
balance -= amount;
console.log(`Withdrew: ${amount}. New Balance: ${balance}`);
}
};
}

const myAccount = createBankAccount(100);
myAccount.deposit(50); // Logs: "Deposited: 50. New Balance: 150"

In the example, the variable balance is encapsulated within createBankAccount and cannot be directly accessed or modified. However, the inner functions deposit and withdraw have access to it.

Maintaining State

One of the most significant advantages of closures is the ability to maintain state. This is especially beneficial when dealing with callbacks, asynchronous operations, and event-driven programming.

For instance, imagine you’re creating buttons dynamically, and each button, when clicked, should display its position:

function createButtons(numberOfButtons) {
for (let i = 0; i < numberOfButtons; i++) {
const button = document.createElement('button');
button.innerText = `Button ${i + 1}`;
button.addEventListener('click', function() {
console.log(`Button ${i + 1} was clicked!`);
});
document.body.appendChild(button);
}
}

createButtons(5);

Because of closures, each button remembers its position, and when clicked, displays its unique number. Without closures, this behavior would be challenging to implement.

Functional Programming and Currying

Closures play a vital role in functional programming, particularly in patterns like currying. Currying is the technique of transforming a function that takes multiple arguments into a series of functions that each take a single argument.

function multiply(a) {
return function(b) {
return a * b;
};
}

const double = multiply(2);
console.log(double(5)); // Logs: 10

In this example, the function multiply returns another function that remembers the value of a due to closures.

In Conclusion

Closures are a foundational concept in JavaScript, bridging the gap between function-oriented and object-oriented programming. They offer mechanisms for data privacy, creating stateful functions, and enabling powerful programming patterns.

Grasping closures ensures you harness the full power of JavaScript, making your code more efficient, clean, and professional. So, the next time you encounter a function remembering its environment or you’re leveraging the concept of private variables, remember that you’re harnessing the magic of closures!

--

--

Patrick Karsh
Patrick Karsh

Written by Patrick Karsh

NYC-based Ruby on Rails and Javascript Engineer leveraging AI to explore Engineering. https://linktr.ee/patrickkarsh

No responses yet