Closures are one of the main pillars of JavaScript. It is also a popular interview question. So, it's really useful to understand what closures are and what we can do with them.
Understanding Closures
Simply said, closures allow accessing variables outside the function. Closure is a combination of a function bundled together with references to its surrounding state. It means that the function can access and manipulate variables located in the outer function's scope even after the outer function has finished. Then if the function is called at a time when the external scope with the variable already doesn't exist, the function still keeps access to the variable. It creates a bubble around our function, it keeps all variables in a scope when the function was created. Let's take a look at the following code snippet. It is a classic example.
const outerFunction = (outerVariable) => {
const innerFunction = (innerVariable) => {
console.log('outerVariable:', outerVariable);
console.log('innerVariable:', innerVariable);
}
return innerFunction;
}
const newFunction = outerFunction('outside');
newFunction('inside');
// logs outerVariable: outside
// logs innerVariable: inside
innerFunction
is a closure that is created when we call outerFunction
and it keeps access to the outerFunction
scope after the outerFunction
was executed.
Closure is like a protective bubble. Closure for the innerFunction
keeps the variables in the function's scope alive as long as the function exists.
Practical examples
Closures are used a lot in JavaScript, even when we don't always notice it. For example in data privacy, factory functions, stateful functions, and can often be found in JavaScript libraries and frameworks.
Now, we will go through two practical examples where we will explain how the Closures work.
Private variables
JavaScript doesn't support private variables but we can achieve a similar behavior by using a Closure. When we don't want to allow direct access to a variable, we can create accessors - the getters and setters - to enable them outside of the function. Let's describe it better in the following example:
const post = () => {
let likes = 0; // private variable
return {
getLikes: () => {
return likes;
},
like: () => {
return likes++;
};
}
}
var post1 = post();
post1.like();
console.log(post1.likes); // undefined
console.log(post1.getLikes()); // 1
var post2 = post();
console.log(post2.getLikes()); // 0
First, we declare the private variable likes
inside the function's constructor. likes
is in the function's scope and isn't accessible from outside. Then we create the accessor method for likes count. Defining the getter we give read-only access to the value. The second method is for incremental increasing the number of likes. No one can change the value more than we allow. In the end, if you check the console, you'll see that we can't get likes
directly. But, we can find out the value by using getLikes()
, or add more likes by calling like()
.
Callback as a Closure
It is another practical example of when we do asynchronous fetching of data and then updating the UI or logging. Check out this example.
const fetchData = (url, callback) => {
// It is simulating an asynchronous operation
setTimeout(() => {
const data = { name: 'Adam', age: 20 };
callback(data);
}, 2000);
}
const processUserData = (user) => {
console.log('Name:', user.name);
console.log('Age:', user.age);
}
fetchData('https://example.com/api/user', processUserData);
fetchData
function takes two parameters. The first one is an url from which we fetch data. The second one is the callback that will be called after the asynchronous call is done. processUserData
function is our callback function and also it is the Closure! When the fetchData
is executed then setTimeout
is called and the callback has access to the outer user data. And the access remains even after the execution of the fetchData
is finished.
This is a common way how callback can access data returned by async fetch and then do various things, for example, update the UI.
Disadvantage
As you can see, the biggest downside is that Closure comes with an extra bag of other variables. This can use up more memory than needed if we're not careful.
Summary
We talked about a Closure as a function having access to variables that were in the outer scope at the time when the closure was defined. Closures have many practical applications. We describe how are used as accessors to private variables - the getters and setters - or together with a callback function for updating the user interface based on asynchronously fetched data.
Top comments (5)
No. ALL functions can do this - a closure is NOT a special kind of function with this ability. From MDN:
Closures and functions are two different things, and saying a function is a closure only serves to create confusion. Every function has an associated closure, but that closure is not the function itself.
@jonrandy You're absolutely right! Thanks for pointing out this mistake. I rephrased the definition as "Closure is a function that can access and manipulate variables located in the outer function's scope even after the outer function has finished."
I believe it's correct since closure is a combination of a function bundled together with references to its surrounding state.
This still implies a closure is a type of function, which it isn't. If this definition were correct, there would be little point in having the two words 'function' and 'closure' - since the definition effectively states they are one and the same. Also, the outer scope doesn't even have to be contained in a function - in fact, it probably can't be limited to just that. A function's lexical scope will also include a chain of scopes up to the global scope (including module scope), so anything in any of these can be accessed too.
I'm probably being a bit of a pedant, but the way closures are taught is generally wrong or distorted by oversimplification. This often leads to confusion for junior developers as they have an incorrect or incomplete understanding of what is going on.
I understand you want to avoid the statement "Closure is a function..." The term "closure" is often used to reference a function that is designed to have access to the outer scope even after it is executed. I think it is absolutely fine.
I reused the definition from MDN as it is "a combination of".
This is probably due to it being taught poorly, since EVERY function has access to it's outer scope. You cannot design or engineer this functionality - it is part of the language.
A family is a combination of people, but any individual in the family cannot be considered as being the family itself. In this case, the family is the closure and the members of that family are the function, and it's lexical environment.
Every function has an associated closure.