JavaScript closures explained
In this blog post, we will dive into the concept of JavaScript closures. We will explain what they are, how they work and how you can effectively use them in your own code.
JavaScript is a versatile and dynamic programming language. It is also a very powerful language with over 98.8% of all websites incorporating it according to W3Techs.
One of the many powerful features within JavaScript are closures. Understanding closures is critical for writing efficient and maintainable code.
In short, closures in JavaScript are features that allow a function to keep access to variables from its outer (enclosing) scope. It retains access even after the scope has finished executing.
What is lexical scoping?
Before we can fully understand the concept of closures, we need to first understand the definition of lexical scoping and its application to closures.
Lexical scoping means that a function’s scope is determined by its position within the source code. Thus, using lexical scoping creates a hierarchical structure which closures can then exploit.
function outer() {
// Declare a variable in the outer function scope
const outerVariable = 'I am the outer function';
function inner() {
// We can access the outer variable here due to lexical scoping
console.log(outerVariable);
}
// Invoke the inner function
inner();
}
// Invoke the outer function
outer();
In the example above, the outer
funtion contains a variable called outerVariable
. Besides the variable we also have another function called inner
inside the outer
function. Even though inner
is invoked outside of its lexical scope, it still has access to the outerVariable
due to said scoping. When we call inner
, it logs the value of outerVariable
to the console.
While simple, this example demonstrates how closures can capture and retain access to variables inide their outer scopes.
So, what are JavaScript closures?
Like we said in the intro of this blog post, a closure in JavaScript is a feature that allows a function to keep access to variables from its outer (enclosing) scope. It retains access even after the scope has finished executing.
Closures occur whenever you define a function inside another function. This creates a relationship between the inner function and outer variables. A closure is a powerful mechanism in JavaScript that provides benefits such as data encapsulation, private variable creation and higher-order functions.
JavaScript closure use cases
- Data Encapsulation: Closures enable the creation of private variables. This promotes data encapsulations which reduces the risk of unintended external interference
- Functional Programming: Closures play a big part in functional programming. This is due to them facilitating te creation of higher-order functions and callbacks
- Event Handling: Closures are most commonly used in event handling scenarios. This is where functions retain access to variables even after the initial event has happened.
Real-world JavaScript closure example
// Function that returns a counter object
function createCounter() {
// Private variable to store the counter state
let count = 0;
// Inner function (closure) that can access and modify the private variable
function increment() {
count++;
console.log(`Counter incremented: ${count}`);
}
// Inner function (closure) that can access and modify the private variable
function decrement() {
count--;
console.log(`Counter decremented: ${count}`);
}
// Inner function (closure) that can access the current value of the private variable
function getCount() {
console.log(`Current counter value: ${count}`);
}
// Returning an object with methods that can interact with the private variable
return {
increment,
decrement,
getCount
};
}
// Creating a counter using the createCounter function
const counter = createCounter();
// Using the counter object
counter.increment(); // Output: Counter incremented: 1
counter.increment(); // Output: Counter incremented: 2
counter.decrement(); // Output: Counter decremented: 1
counter.getCount(); // Output: Current counter value: 1
In the example above we created a createCounter
function. This function returns an object with three methods: increment
, decrement
and getCount
. All these methods are closures in this example. What this means is, that they have access to the private variable count
which is declared in the outer
function.
When creating this counter using createCounter
, you get an object counter
with methods that can mutate the counter’s state. The private variable count
is hidden from the outside world, meaning other functions, which are not closures of our createCounter
can not access it directly. This demonstrates the concept of data encapsulation that can be achieved through the help of closures.
Conclusion
While JavaScript closures might seem complex, mastering them opens up a door to write more modular, scalable and maintainable code. This blog post aimed to demystify closures in order to provide you with the knowledge of closures. We hope this brings more confidence into using them in your own projects!