Closures in JavaScript

In JavaScript closures are everywhere. They enable you to write more expressive and better code. You may not have even realized that you are already using closures. At first, the word closure sounds quite intimidating, however, once you understand what it is and how to use it, it becomes second nature.

If you want to become a developer who uses advanced concepts and techniques, aces job interview and gets great job offers then you simply cannot afford to ignore and must master closures.

Let’s dive right in.

In JavaScript there are 3 different scope levels, a function can access its own scope, a function can access the outer scope and global scope. If you remember every time a function is created, a new scope is created (function scope) and all of the data inside that function is only accessible inside that scope. So this means that we cannot access the function’s data from outside.

A closure in JavaScript is a function that has access to the scope it was created outside of (outer scope). In other words, the function remembers where it was created (the environment it was created in) and keeps a reference to it. One important thing to note here is that the access to the outer environment is by reference, not by value (we’ll come back to this later). Something else to remember is that when a function is created in JavaScript it technically becomes a closure.

Let’s have a look at a quick example:

function outer() {
  const outerEnvironment = 'outer';

  function inner() {
    return `I have access to ${outerEnvironment} environment`;
  }

  return inner;
}

const outerFunc = outer(); // we are storing function declaration
outerFunc(); // we are invoking the function

Take some time to go through the code. What do you expect to happen here? The function inner() has (lexical scope) access to its outer environment (scope), it closes over the scope of outer(). Note that we are not invoking inner() but returning its function object (all functions are objects in JavaScript, remember?). We are then storing the function object in a variable and invoking it on the next line. The output in the console should be: ‘I have access to outer environment’.

Usage / gotchas

Encapsulation

One of the practical uses of closures in JavaScript is data encapsulation (privacy). You associate data with function(s) that operate(s) on that data. If we compare this to object-oriented programming then data is the object’s property, a function is a method. Essentially with closures, we can have many functions and variables inside the function but only expose what we need to by creating a public facing API. This way everything else other than the exposed API will be kept private. This is known as the module pattern. A module in JavaScript is just an organized, self-contained portion of code. And since JavaScript natively does not support classes the module pattern solves this.

Let’s have a look at another example:

function bankAccount() {
  let balance = 0;

  function updateAccount(operation, amount) {
    switch(operation) {
      case 'deposit':
        balance += amount;
        break;
      case 'take-out':
        balance -= amount;
        break;
      default:
        break;
    }
  }

  return {
    deposit: (amount) => {
      updateAccount('deposit', amount);
    },
    takeOut: (amount) => {
      updateAccount('take-out', amount);
    },
    getBalance: () => {
      console.log(balance);
    }
  };
}

const account = bankAccount();
account.deposit(10);
account.getBalance();

In this short example, we are emulating a very simple bank account. We have a function bankAccount(), inside of this function (in its scope) we are storing a variable balance and a function that updates the balance – updateAccount(). Finally, we are returning an object which allows us to add, subtract and retrieve the balance. Essentially, only the desired functionality is being exposed this way. When you run this code, you should see the account balance – 10. If we try accessing updateAccount() we’ll get an error. Why you might wonder?

Well, we are accessing the parent scope from the object that we are returning but protecting the guts of updateAccount(). The three functions that we are returning are closures and have access to updateAccount method due to the lexical scoping. There is also a single lexical environment which is shared by these functions. This way we are able to only expose we want and protect the rest.

It is also worth mentioning the drawback of closures. As closures capture variables inside and keep a reference to the parent environment, the environment gets stored in memory and cannot be garbage collected unless all of the references to it are removed. If we are creating a large number of closures without destroying them we will start introducing memory leaks. Memory that is not required anymore by an app and it is not returned back to the operating system is a memory leak. It can cause an application to slow down or crash.

That’s it for this post and I hope that all of the above made sense and wasn’t too complicated.