DEV Community

loading...
Cover image for Closures and their practical uses

Closures and their practical uses

Sekab
A software engineer with 3+ years of experience who loves working with JavaScript and loves refactoring to the point that I look weekly into old codebase of mine and refactor some parts of it!
・3 min read

What the heck are closures anyways?

According to the official Mozilla website 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.

When I first read this the first time I couldn't fully understand what it actually means or how closures function in JavaScript or pretty much any other programming language.

I would have loved it if someone explained it to me in a simpler way.
Basically, a closure is a way that allows functions that are living inside outer functions to access the outer function's scope, the scope could be either variables or even other functions that live inside of it.

function outer() {
  let name = "Amr";
  function inner() {
    console.log(name);
  }
  inner();
}

outer();
Enter fullscreen mode Exit fullscreen mode

In the above snippet there is a declaration of a function called outer and inside of it we declared two things:

  • A variable called name
  • Another function called inner

We then invoked the inner function inside the outer function scope and logged the variable name, if we thought about the output of such code we would think that this code snippet is wrong because there is no variable called name inside the inner function scope, thus the console would definitely output undefined.

At this exact point closure shines, the idea here is that function inner has an access to the scope of the outer function, meaning that it actually sees the variables or objects declared inside of our beloved outer function and this also would work if we did something like this:

function outer() {
  let name = "Amr";
  function inner() {
    console.log(name);
  }
  return inner;
}

let innerFunc = outer();
innerFunc();

Enter fullscreen mode Exit fullscreen mode

After returning the inner function when invoking the outer function, we could invoke the inner function and still get the value declared before in our outer function and that is the whole point of closures.

Uses of closures

Now comes the fun part, where on earth on our codebase could we use such a strong feature?

Let us start by thinking about a very basic scenario, what if we have an application that contained a counter, and whenever the user does something (let us say clicks a button ) the counter is incremented by 1.

A basic solution for such a scenario would be like this:

let counter = 0;

function incrementCounter() {
  counter++;
}
Enter fullscreen mode Exit fullscreen mode

Here we defined a global variable for our counter and a function called incrementCounter that increments our counter by one.
This solution would work but the problem here is that our counter is defined globally and this could allow other functions to manipulate the counter current value thus making our code less maintainable and at risk of bugs.

Here closures would solve such problem by the following code:

function counterClosure() {
  let counter = 0;
  function increment() {
    counter++;
  }
  return increment;
}

let increment = counterClosure();
increment();

Enter fullscreen mode Exit fullscreen mode

Now we defined our counter variable inside a function called counterClosure and whenever we want to increment it we can simply call the inner function which is called increment and the counter variable would be enclosed and not manipulated by other functions.

this could even be refactored a little bit:

function counterClosure() {
  let counter = 0;
  function increment() {
    counter++;
  }
  function getCounter() {
    return counter;
  }
  return {
    increment: increment,
    getCounter: getCounter,
  };
}

let { getCounter, increment } = counterClosure();
increment();
console.log(getCounter()); // 1
Enter fullscreen mode Exit fullscreen mode

Now we can get access to our counter using the getCounter function returned.

Another use case which I personally used before if what if we needed to create HTML elements and pass content to these elements whenever we are creating them?
lets look at how closures would solve this:

function elementCreator(open, close) {
  return function (content) {
    return `${open} ${content} ${close}`;
  };
}

const br = elementCreator("<div>", "</div>");
const header = elementCreator("<header>", "</header>");

const headerContent = header("Hello this is a header");
console.log(headerContent); // <header> Hello this is a header</header>

Enter fullscreen mode Exit fullscreen mode

Here we defined a function that simply returns another function that takes content as a param, this is very handy as we now created functions for creating div and header elements and we just need to pass content to such functions, we could also use composition to even pass another element inside, for example, the div element like this:

const divWithHeader = header(div("hello div"));
// <header> <div> hello div </div> </header>
Enter fullscreen mode Exit fullscreen mode

Conclusion

This is it for this post about closures, there are numerous other uses of closures that I did not cover today but in the end, I would definitely encourage using closures in scenarios like encapsulating logic ( defining private variables ) and in composition ( This comes very handy if you are in love with functional programming )

Discussion (1)

Collapse
z2lai profile image
z2lai

That's a very clean way of creating markup templating functions! Do you know if this is the standard way of creating markup with vanilla JS/jQuery? I just started and all the markup templating is hardcoded with jQuery :/.