DEV Community

Cover image for Understanding closures
João Guilherme do Amaral Vequiato
João Guilherme do Amaral Vequiato

Posted on • Updated on

Understanding closures

Quickly definition to who come from google searching "What is closure"

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.

In other words, it's a function that is able to remember the environment that was created.

Also known as "Closed over Variable Ambiente" (C.O.V.E) and "Persistent Lexical Scope Referenced Data" (P.L.S.R.D).

Ok, but what that means?

When we execute a function in Javascript, that function creates a brand new one execution context to it, with a local memory, variable ambiente and a state.

What happens is that when the function execution it's concluded, all of this context is excluded, including your local memory (which owns all of arguments that we passed for this function). Except the value that it returns.

But what if we could create "functions with memories". Functions that able persist data, like if could we store a state for it. This is the one of the most powerful concepts in Javascript, closures.

Functions with memories 🧠

This can be a little abstract in this point (how the closures can be so powerful), but keep in your mind in this point that so much concepts using closures under the hood, like memoization functions, module pattern, iterator, currying and much more.

Applying closures concept 🔥

Pay attention in the codes below, what we'll do it's called function decorator, which make us able to "edit" our functions (this is not the moment topic, soon i'll make one article series about functional programming).

const multiplyBy2 = (number) => number * 2;
Enter fullscreen mode Exit fullscreen mode

Ok, nothing new until now.
Suppose we need to make with that our multiplication function, in a certain context, can be used only one time.

Can we create a counter inside of function for in the case when counter > 0 it doesn't execute? This is not possible, because every time that function ends its execution its local memory is destroyed.

So, lets create a new function.

const oncefy = (fn) => {
  const counter = 0;
  const myFunction = () => {
   if (counter === 0) {
     fn();
     return;
   }
   console.log("Only one time");
  }
}
Enter fullscreen mode Exit fullscreen mode

Our function oncefy receives one function like parameter, define a counter and verify if counter is equal 0, if is equal 0 execute our argument, else our function prints in the console.

Let's apply the closure concept in our function multiplyBy2, passing it like argument for our function oncefy, wich is responsable to memorizer our counter.

const multiplyBy2 = (number) => {
  console.log(number * 2);
}
const oncefy = (fn) => {
  let counter = 0;
  const myFunction = (number) => {
    if (counter === 0) {
      fn(number); 
      counter++; 
      return;
    } 
    console.log("🍃");
  }
  return myFunction;
}
const multiplyBy2Once = oncefy(multiplyBy2);
multiplyBy2Once(3);
multiplyBy2Once(3);
Enter fullscreen mode Exit fullscreen mode

Now, the function multiplyBy2Once will be executed only one time and nevermore. We "edit" our function multiplyBy2, giving to it a new behavior.
However, we don't need to change its original code, maintaining our function reusable.

How it works ⚙️

Let's understand step by step in the code above how closures work.

  1. We stored one const called multiplyBy2 in the global memory, and its value its one function that receives one parameter called number and returns number * 2.

  2. We stored one const called oncefy in the global memory, and its value its one function that receives one parameter called fn and returns one const called myFunction.

  3. We declared one const called multiplyBy2Once, and its value we don't know yet because now we need execute the function oncefy first to know what it returns and atribute the return as the value.

  4. In the moment that oncefy its executed, the interpreter creates a new execution context for this function.
    The first thing is take all function parameters (in this case multiplyBy2) and stores in the local memory of this context.
    Now the local memory have one const called fn that have multiplyBy2 function as value.
    Next step it's take all declarations inside the function and stores in the local memory (in this case, one let counter with value 0 and one const myFunction with value as one function that receives one parameter called number).
    After all declarations, it finally returns the const myFunction.

  5. When return our value myFunction, all the execution context (including the local memory) are removed, except the returned value, that now it's the value of the const multiplyBy2Once.

  6. On the next line of our code we execute the function multiplyBy2Once which, in the realite it's the function myFunction returned by oncefy

  7. A new execution context is created for our function and again the first thing is take all function parameters of this function and stores in the local memory.

  8. Now our local memory have one label called number and have the value the argument 0

It's in this moment that things become interesting.
In the next line of the execution, we have the condicional if (counter === 0)
So the interpreter goes to the function's local memory searching for the counter variable, but this variable doesn't exist in this execution context.

It's now that closures make the magic.
When a function is defined, it gets a bond to the surrounding local memory
(“variable environment”) in which it has been defined.
So, because the function myFunction was defined inside of function oncefy, the function myFunction "stores" all local memory of the context where that it's defined, including the const counter.

When the interpreter doesn't find counter in the actual execution context scope, it goes up to the "backpack" that myFunction carries with itself (aka closure).

By keeping that memory, this context don't be erased, always remembering its last execution

Conclusion 💡

Closure it's a very important concept in programming and can be used for much things.

Understand how it works may not be one easy task (and hard to explain too).

However, it's important understand closures to understand more complex concepts and develop powerful solutions.

See you soon!👋

If you lost:
Understanding classes and prototypes
Understanding Higher Order Function

Top comments (2)

Collapse
 
janclover32 profile image
justanothercodelover

very well explained. i hear before this concepts: bond, backpack
frontend masters?

Collapse
 
jgamaraalv profile image
João Guilherme do Amaral Vequiato

Yep. Will Sentance. The master! Hahahahaha