DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’»

DEV Community πŸ‘©β€πŸ’»πŸ‘¨β€πŸ’» is a community of 970,177 amazing developers

We're a place where coders share, stay up-to-date and grow their careers.

Create account Log in
wrongbyte
wrongbyte

Posted on • Updated on

What are closures?

The concept of closure is important when it comes to understanding how languages such as Javascript work - and how scopes, lifetimes and references come together and can be combined to provide useful features to programmers.

Variables and their scopes

1 - Local and global scope

Every time you use a variable inside a function, this variable needs to be declared somewhere - and this place can be the local scope (inside of the function) or the global scope.
Variables declared in the local scope are only accessible inside of their parent function - which means you cannot reference them outside of the function:

function() {
  let a = 1;
  console.log(a); // works
}    
console.log(a); // fails
Enter fullscreen mode Exit fullscreen mode

In the example above, written in JS, we get an Uncaught ReferenceError: a is not defined.

However, by declaring a as a global variable, we can reference it anywhere in our code:

var a = 1;
function() {
  console.log(a); // works
}    
console.log(a); // works
Enter fullscreen mode Exit fullscreen mode

2 - Lifetime of entities

The behavior above happens because entities (variables, constants, etc) have different lifetimes across a program depending on their scope. The definition of the "lifetime" of an entity can be thought as the period from its creation (allocating memory for it) to its destruction (deallocating the memory used for this variable).

A global variable has the same lifetime of the entire program. Local variables, however, have specific lifetimes, so they may not be "alive" during the entire program’s lifecycle.
Hence, the underlying programming language must provide a way to deal which such cases, such as manual memory management or some kind of garbage collector. Otherwise, one could stumble upon a dangling reference when not careful enough, since deallocated variables cannot be referenced.

This dynamic is important to understand because we'll eventually get into a tricky question:

What would happen if we wanted to reference a variable outside of its original scope?

3 - Nested functions

There is a special use case of functions in which we face the question above.
Let's say we wanted to create a function inside of another function. Let's call them outer and inner.

const outer = function() {
  const inner = function() {
     console.log("hello");
  }
}
Enter fullscreen mode Exit fullscreen mode

Everything declared inside of inner has now access to the scope of outer.
It means that we can declare variables inside of outer and then use them inside of inner, as it follows:

const outer = function() {
  const a = 1;
  const inner = function() {
    console.log(a);
  }
  return inner;
}

var fnc = outer(); // execute outer to get inner 
fnc();

Enter fullscreen mode Exit fullscreen mode

We can then return inner from outer. This part is important because it permits inner to leave the scope of outer.
After that, we assign the return of outer to a variable fnc, which we can execute to get inner.

Therefore, we now have all variables of inner, declared in outer, closed under this function.

What we'll seeing here is basically the implementation of closures in Javascript: a function that has access to the additional data from its surrounding scope.

Therefore, we can define a closure as:

1 - A function and
2 - A reference to that function's outer scope
Enter fullscreen mode Exit fullscreen mode

In the example above, our function is inner and the outer scope is the scope of outer, which has the variable a. Without keeping a reference to this surrounding scope, we would not be able to retrieve the value of a. Once we have a reference to this surrounding scope, we have a closure.

Back to scope and lifetimes: what would happen if we didn't have closures?

There are languages that don't implement closures.
C is a good example: in C, you won't have closures as well as native support for nested functions.
Using the simplified definition we've seen before, we can say that what's needed to implement closures is a record of the function along with its environment.
To do this in C, there would be necessary to use structs, function pointers and a lot of fuzzy code. Furthermore, it would be necessary to have support for nested functions, which C doesn't have by default. Since closures are only meaningful when a function needs to be executed outside of the scope in which it was originally declared, having nested functions is essential for this concept to work.
A lot of work, huh?

How Javascript solves it?

We already learned that JS implements closures and what they are. But how does it work under the hood?

Every function in JavaScript maintains a reference to its outer environment. This reference is used to determine the execution context of the function when it is invoked, and therefore enables the function to "see" variables declared outside of it.
It creates a kind of state particular to the functions declared in the same lexical environment.

πŸ’‘ The lexical environment consists of any local variables in the function’s scope when the function is created.

In addition, if we have functions that call other functions that in turn call even more functions, JS creates a chain of references to the outer lexical environments - which is called the scope chain. Therefore, closures in Javascript can become quite useful - and we are going to see a few uses for them now.

Why are closures useful?

If you have ever programmed using an object-oriented language, you will probably know the concept of classes and states.
Once you use a closure, you are creating the equivalent of a private state associated with a function.

function foo() {
  const secret = Math.trunc(Math.random() * 100)
  return function inner() {
    console.log(`The secret number is ${secret}.`)
  }
}
const f = foo() // 
f()
Enter fullscreen mode Exit fullscreen mode

In the example above, secret is not directly accessible from outside foo. foo then creates a kind of encapsulation similar to what we see in OOP, which is useful in several cases. Furthermore, JavaScript did not have a class syntax until 2015 - making the use of closures an alternative.

Finally, there are several other use cases for closures - for example in functional programming, event-oriented programming and so on.

πŸ’‘ Further reading: Nested functions, closures and first-class functions

Top comments (12)

Collapse
 
crowdozer profile image
crowdozer • Edited on

Something to add: you can create new scopes anywhere you want, provided the compiler doesn't think you're trying to initialize an object. A function body isn't necessary.


const x = 'foo'
console.log(x) // 'foo'

{
  const x = 'bar'
  console.log(x) // 'bar'
}

console.log(x) // 'foo'
Enter fullscreen mode Exit fullscreen mode

Though, doing that looks ugly and probably isn't ideal. An example of a valid use case is switch statements, it allows you to isolate each case into its own local scope.

Collapse
 
wrongbyte profile image
wrongbyte Author

You are right, this is also something interesting about scopes. Thanks for pointing out!

Collapse
 
wiseai profile image
Mahmoud Harmouch • Edited on

In ReactJS, my first encounter with closures was in form of arrow functions:

closure

I was so confused at first, then i was like: wait a sec, it is just something that returns something that in return returns something. Aha! Know what i am sayin'?

Edit: For the sake of correctness, I think this statement is somewhat wrong:

there are several other use cases for closures - such as currying

Currying is the broader term, and closure is just a practice of it in programming.

Collapse
 
wrongbyte profile image
wrongbyte Author

Thanks for pointing out! I've updated the post πŸ˜†

Collapse
 
____marcell profile image
Marcell Cruz

Your posts are getting better and better every time, really good explanation and examples

Collapse
 
wrongbyte profile image
wrongbyte Author

Tnx, I'm really glad to help 😊

Collapse
 
daniloab profile image
Danilo Assis

nice post, congratz

Collapse
 
wrongbyte profile image
wrongbyte Author

Thanks 😊

Collapse
 
incrementis profile image
Akin C.

Hello wrongbyte,

Thank you for your article.
I read something about closures years ago.
Reading your article helped me refresh some of my knowledge about closures.

Collapse
 
wrongbyte profile image
wrongbyte Author

I'm glad I could help! πŸ˜„

Collapse
 
darkwiiplayer profile image
π’Š©Wii πŸ’–πŸ’›πŸ’šπŸ’™πŸ’œπŸ’πŸ’Ÿ

Nice post. Closures are one of the most useful concepts to understand in languages that have them.

Collapse
 
wrongbyte profile image
wrongbyte Author

For sure! And they are very interesting

Need a better mental model for async/await?

Check out this classic DEV post on the subject.

β­οΈπŸŽ€ JavaScript Visualized: Promises & Async/Await

async await