What exactly is closure?

Published on

You might have heard the term "closure" or being asked about it in a job interview. But what exactly is closure? What is it used for and how does it work? In this article, we'll take a look at the concept of closure in Javascript.

What is closure?

Based on the definition 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.

An other definition from Kyle Simpson's course Deep JavaScript Foundations which I found it more straightforward:

A closure is when a function "remembers" its lexical scope even when the function is executed outside that lexical scope.

How does it work?

Okay, let's break it down. For the first part of the definition, it's pretty straightforward that an inner function has access to the variables of the outer function.

const outer = () => {
  const message = 'Hello, '
  const inner = () => {
    console.log(message + 'world!')
  }
  return inner
}

const inner = outer()
inner() // Hello, world!

In the given example, the inner function has access to the message variable from the outer function. This is because the inner function is created inside the outer function and it has access to the variables of the outer function.

For the second part, it says that closures are created every time a function is created. This means that every time you create a function, a closure is created along with it.

const outer = () => {
  let count = 0
  return () => {
    count++
    console.log(count)
  }
}

const firstIncrement = outer()
const secondIncrement = outer()

firstIncrement() // 1
firstIncrement() // 2
SecondIncrement() // 1

In this example, we have two closures created by calling the outer function twice. Each closure has its own count variable that is independent of each other.

Could you guess the output of the following example?

function outer() {
  let firstCount = 0

  function firstInner() {
    firstCount++
    console.log(firstCount)
  }

  let secondCount = firstCount

  function secondInner() {
    secondCount++
    console.log(secondCount)
  }

  return [firstInner, secondInner]
}

const [firstIncrement, secondIncrement] = outer()
firstIncrement()
firstIncrement()
secondIncrement()

Let's give it a try and see if you can guess the output.

. . .

The output will be: 1 2 1. If you guessed it right, congratulations! If not, don't worry, it's a bit tricky.

When the outer function is called, the closure is created for both firstInner and secondInner functions. Even though they share the same closure but at the time of closure creation, the secondCount variable is assigned the value of firstCount which is 0. So, when the secondIncrement function is called, it increments the secondCount variable from 0 to 1 instead of 2 to 3.

What is it used for?

Closures are widely used in Javascript. You might have used it without even realizing it. Here are some common use case of closures:

  • Data privacy: You can create private variables using closures. This is useful when you want to hide some data from the outside world.
const counter = () => {
  let count = 0
  return {
    increment: () => count++,
    decrement: () => count--,
    getCount: () => count,
  }
}

In the above example, the count variable is private and can only be accessed through the returned object. This way, you can prevent the count variable from being modified from the outside. In the future, if somehome the count variable gets an expected value, you can easily debug it because it's only modified by the increment and decrement functions.

  • Currying: Closures are used in currying. Currying is a technique of evaluating a function with multiple arguments into a sequence of functions with a single argument.
const add = (a) => (b) => a + b

const addOne = add(1)
const three = addOne(2) // 3

In the above example, the add function returns another function that takes a single argument. This is possible because of closures. The inner function (b) => a + b has access to the a variable form the outer function.

  • Event listeners: Closures are used in event listeners. When you add an event listener to an element, you pass a callback funciton to it. And this callback function has access to the variables of the outer function.
const message = 'Hello, world!'
const button = document.querySelector('button')
button.addEventListener('click', () => {
  console.log(message)
})

In the above example, the event listener callback function has access to the message variable. So whenever the button is clicked, the callback function will be triggered and log the Hello, world! message to the console no matter when and where the callback fuction is executed, that's the power of closures.

Conclusion

Closure is a powerful concept. Key poins to remember about closures are:

  • Access to the variables of the outer function (its lexical scope) from an inner function.
  • The closure is created every time a function is created.
  • Wherever you pass the function, it continues to have access to the origin scope where it's defined.

Comments