DEV Community

Cover image for JavaScript Closures simply explained
Adam Blazek
Adam Blazek

Posted on • Edited on

JavaScript Closures simply explained

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

Enter fullscreen mode Exit fullscreen mode

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 bubble
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
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
jonrandy profile image
Jon Randy 🎖️ • Edited

Closure is a function that can access and manipulate variables located in the function's scope at the time when the function was defined

No. ALL functions can do this - a closure is NOT a special kind of function with this ability. From MDN:

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.

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.

Collapse
 
princam profile image
Adam Blazek

@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.

Collapse
 
jonrandy profile image
Jon Randy 🎖️ • Edited

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.

Thread Thread
 
princam profile image
Adam Blazek

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".

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.

Thread Thread
 
jonrandy profile image
Jon Randy 🎖️ • Edited

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

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.