Intricacies of Closures in Javascript
Hey peeps!
Today, we're diving into a fascinating and somewhat magical aspect of JavaScript - closures.
So buckle up, and let's unravel the mysteries together.
The Essence of Closures
At the heart of JavaScript lies the concept of closures. Imagine a function bundled along with its lexical scope, creating a magical bond. This phenomenon is what we call a closure. To make it more concrete, let's take a look at a simple example:
function createClosure() {
let outerValue = 'I am from the outer function';
function innerFunction() {
console.log(outerValue);
}
return innerFunction;
}
createClosure();
// Output: I am from the outer function
In the innerFunction
, a closure is formed with the variable outerValue
, which is part of the lexical scope of createClosure
. This implies that innerFunction
has access to its parent's lexical scope, creating a captivating connection.
Functions: The Heart of JavaScript
Functions are the backbone of JavaScript, and they can be expressed in various ways. The fact that functions can be used as Javascript with various forms, sometimes makes closure a complex topic to understand.
For example, returning a function from a function.
Return a Function
function x() {
var a = 7;
function y() {
console.log(a);
}
return y;
}
x();
The intriguing part here is figuring out what this returns and what the output might be. Can you guess?
The Complexity of Closures
Closures can add complexity to your code, but they also bring powerful capabilities. Consider the following scenarios:
Closures in Action
function x() {
var a = 7;
return function y() {
console.log(a);
};
}
var z = x();
console.log(z);
z();
In this snippet, not only is the function returned, but the entire closure (function y
along with its lexical scope) is stored in z
. So, even when z
is used elsewhere in the program, it remembers the variable a
inside x()
.
The Reference Persists
function referencePersists() {
let outerValue = 7;
function innerFunction() {
console.log(outerValue);
}
outerValue = 100;
return innerFunction;
}
const persistedReferenceFunction = referencePersists();
console.log(persistedReferenceFunction); // Output: function () { console.log(outerValue); }
persistedReferenceFunction(); // Output: 100
Here, outerValue
doesn't refer to the value 7
; it refers to a
's reference. The value 7
doesn't persist, but the reference to outerValue
does.
A Deeper Example 1:
function createCounter() {
let count = 0;
function increment() {
count++;
console.log("Count incremented:", count);
}
function decrement() {
count--;
console.log("Count decremented:", count);
}
function getCount() {
return count;
}
return {
increment,
decrement,
getCount,
};
}
const counter1 = createCounter();
const counter2 = createCounter();
counter1.increment(); // Output: Count incremented: 1
counter1.increment(); // Output: Count incremented: 2
counter2.decrement(); // Output: Count decremented: -1
console.log(counter1.getCount()); // Output: 2
console.log(counter2.getCount()); // Output: -1
Closures enable each counter object to have its own encapsulated state, preventing external access to the count
variable. This is a prime example of how well closures work when defining private variables and functions inside factory functions.
A Deeper Example 2:
function deeperExample() {
let outerValue1 = 900;
return function () {
let outerValue2 = 7;
function innerFunction() {
console.log(outerValue2, outerValue1);
}
innerFunction();
};
}
const deepClosure = deeperExample();
deepClosure(); // Output: 7 900
The function innerFunction
forms a closure with the scopes of both function
and deeperExample
, creating a chain of closures.
A Deeper Example 3:
function createMultiplier(base) {
return function (multiplier) {
return function (value) {
console.log(`Result: ${base * multiplier * value}`);
};
};
}
const doubleMultiplier = createMultiplier(2);
const tripleMultiplier = createMultiplier(3);
const doubleAndTriple = tripleMultiplier(2);
doubleAndTriple(5); // Output: Result: 30
In this example, closures enable the innermost function to access not only its local variables (base
, multiplier
, value
) but also the variables from its parent functions (base
and multiplier
). This creates a chain of closures, allowing for a powerful and flexible function factory.
Summing It Up
In a nutshell, a closure is a function that retains access to its outer function's scope even after the function has returned. It's like a piece of magic that keeps the connection alive.
Thank youu :)
Subscribe to my newsletter
Read articles from Prerana Nawar directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by