Why you need to understand JavaScript Closures
A closure is the combination of a function and the lexical environment within which that function was declared 1.
function count() {
var counter = 0;
return function innerCount() {
return ++counter;
}
}
var counter1 = count(); //Create a new counter
var counter2 = count(); //Create a new counter
counter1(); //returns 1
counter1(); //returns 2
counter2(); //returns 1 again, but why!?
What happened there?
If you, like many others, thought that the last line would return 3 then this article just might help you a little bit. What happens is that every time we call count() we are returning a new copy of innerCount function and each copy of innerCount relies on data that is outside of itself, outside of its own scope.
That environment data as you may call it is crucial for the execution of the function itself. We can say that it depends on it to work. If, when we run innerCount, we don’t have access to counter then the whole function would be broken! But by the time innerCount is executed, counter is no longer accessible to the outside world!
JavaScript comes to the rescue and it wraps, groups or, if you want to say it right, creates a closure around the function and all the data it needs to access, its lexical environment.
If counter was a variable in the global scope, then all the counters will be increasing the same variable instead of one inside a closure. Feel free to try it out by extracting counter to above line 1
Creating a Function Factory as a real world example
We can take advantage of the closure behaviour by creating a function that, depending on the arguments passed, returns a function with a different execution logic. The simplest example I can think of would be a factory that will return the power of n for a given number. The result would be the same as calling Math.pow(subject, power)
function powerOfNFactory(power){
return function generatedFunction(subject){
return Math.pow(subject, power);
}
}
What this snippet does is create a dynamic function, in essence a function whose algorithm is not explicitly defined in the code itself but rather at run-time when it’s created with the arguments passed to the factory. Of course with the previous example you might be thinking that it’s rather useless and you’d be right, but what you need to grasp is the potential this functionality has.
We can now define a function that gives us the power of two by simply calling var powerOf2 = powerOfNFactory(2). The internal generatedFunction will keep a closure over the power variable and will be completely independent from other calls to powerOfNFactory. This means we can extend the functionality and do things such as keeping track of the number of times our dynamic function was called, adding memoization (caching the results in order to prevent the repeated execution when called with the same arguments) and much more.
Adding the counter
The same way our dynamic function has access to the argument passed to the factory function, it will have access to anything defined inside of it just like on the first example with the counter, allowing us to define a simple counter and in doing so keep track of the times we’ve called this. Maybe you’d like to do this for logging purposes, maybe you’d want to limit the number of times a certain functionality can be executed before you do something else, up to you.
function powerOfNFactory(power){
var counter = 0return function generatedFunction(subject){
counter++;
console.log(`Dyanmic Power of ${power} called ${counter} time/s`);
return Math.pow(subject, power);
}
}
var powerOf2 = powerOfNFactory(2);
var powerOf3 = powerOfNFactory(3);
powerOf2(2); // Returns 4, logs "Dyanmic Power of 2 called 1 time/s"
powerOf2(2); // Returns 4, logs "Dyanmic Power of 2 called 2 time/s"
powerOf3(3); // Returns 27, logs "Dyanmic Power of 3 called 1 time/s"
Memoization
Since the code executed inside our function is not expensive in terms of time we shouldn’t have to worry about caching its execution but it’s easier to show it here than it would be on a more extensive example so bare with me. This is useful for operations that will always return the same value when called with the same arguments but take more resources in terms of execution time for example
function powerOfNFactory(power){
var counter = 0;
var cache = {};
return function generatedFunction(subject){
counter++;
console.log(`Dyanmic Power of ${power} called ${counter} time/s`);
if (cache[subject] === undefined) {
cache[subject] = Math.pow(subject, power);
} else {
console.log("Cached!");
}
return cache[subject];
}
}
var powerOf2 = powerOfNFactory(2);
var powerOf3 = powerOfNFactory(3);
powerOf2(2); // Returns 4, logs "Dyanmic Power of 2 called 1 time/s"
powerOf2(2); // Returns 4, logs "Dyanmic Power of 2 called 2 time/s" "Cached!"
powerOf3(3); // Returns 27, logs "Dyanmic Power of 3 called 1 time/s"
Notice that we define a cache variable outside of the dynamic function definition, the same way we did with counter. We will be populating that object with the keys received as parameters when calling the dynamic function and storing the value in order to skip the execution of, in this simplified case, Math.pow(subject, power).
Summary
I’m sure that by now, or at least I hope so, you now have a better understanding of what closures are and what they can be useful for. It’s something that we should not be scared of and once you understand them, and why they work the way they do, you can start taking advantage of the full potential that comes with them.