According to developer.mozilla.org

A closure is the combination of a function and the lexical environment within which that function was declared.

Closures might look like magic at first but they are not really hard to understand if you know how the core concept works.

Probably one of the best features in javascript is that every function in JavaScript has a closure. When you define a function you basically create a closure. Once you execute the function, its closures enables the function to access data in its scope.

If you want to understand closures and the role they play in JavaScript, you first need to understand a few other simple concepts about JavaScript functions and their scopes.

Scopes can be shared between multiple closures

When we define multiple functions in the same scope, that scope is shared among all created closures including the global scope which is always shared among all closures.

For example:

function userProfile() {
  let age = 20;
  
  function doubleAge() {
    age = age + age;
    console.log(name);
  };
  
  function squareAge() {
    age = age * age;
    console.log(age);
  }
  
  return { doubleAge, squareAge }
}
let { doubleAge, squareAge } = userProfile();
doubleAge(); // prints 40
squareAge(); // prints 1600
doubleAge(); // prints 3200

Functions are assigned by value reference

Javascript has 3 data types that are passed by reference: Array, Function, and Object. These are all technically Objects, so we’ll refer to them collectively as Objects.

For example:

function helloWorld() {
  console.log('Hello World');
};
const hello = helloWorld;

The code above will assign the constant hello a reference to the function helloWorld. This is a reference and not a copy. Anything you do to the reference will affect the function itself. For example:

hello.name = 'Andy';
console.log(helloWorld.name);

The code above will print Andy.

Closures can span multiple scopes

While scopes are created when you execute a function, closures are created when you define the function. Closures will never go away once you execute the function and you will have access to the data in the closure even after the function is defined and gets executed.

Given the example below:

const name = 'Andy;
// Let's define a function and create a closure
function userProfile() {
  let role = 'Engineer';
  
  // Define another function and create a closure
  function companyDetails() {
    const company = 'IBM'
    console.log(name, role, company);
  }
  
  companyDetails();  // will print: Engineer, IBM, Andy
  // The companyDetails closure will not get discarded
  role = 'Designer';
  companyDetails();  // will print: Designer, IBM, Andy
}
userProfile();

How it works? When we define the companyDetails function, its created closure will allow us to access the scope of companyDetails plus the scope of userProfile plus the global scope. BUT because the closures are giving us a reference to those constants instead of a copy, if their values are changing after the closure is created, we will always get the new value when the function is executed.

Closures have read and write access

Because we get references to the variables inside closures it means we have both read and write access.

Scopes have a lifetime

Every time you call a function, you are actually creating a new scope during the execution of that function. Scopes are created and discarded right away except the global scope, which will not go away as long as the application is running.

One more example

Here is an example showing that local variables are not actually copied and we’re actually accessing the reference.

function person() {
  let age = 20;
  const saySomething = function() { console.log(age); }
  age++;
  return saySomething;
}
const personDetails = person;
personDetails(); // will log 21

Here are some final points

– Every time you use function inside another function, a closure is used.
– Closures in JavaScript are basically like keeping a copy of all the local variables, exactly like they were when a function exited.
– It is possible to have nested function declarations and get closures at more than one level.