Igor Šarčević wrote this on November 18, 2015

Closures are not magic

Several years ago, while I was still a high school student, a friend of mine introduced me the concept of closures. I didn’t understand even a bit of what he wanted to show me, but he looked really hyped when he talked about them. To me, it all looked like some kind of deep magic. Even Google didn’t help. All I could find were scientific papers, that were incomprehensible for a high school student.

I giggle a bit when I think back at my high school programming struggles. This article is an attempt to explain closures in simple terms, that would help my old high school self to easily grasp their power.

Counting events

We’ll start with a simple problem, that can be easily solved by introducing closures in our program.

Let’s say that we want to create a mechanisms for counting events. Such a mechanism would help us to keep track of our code’s execution, or even while trying to debug some issues. For example, we could use it like in the following example:

increment();  // Number of events: 1
increment();  // Number of events: 2
increment();  // Number of events: 3

As you can see in the above example, our desired code would display a message “Number of events: x” every time we execute the increment() function. Here is a simple attempt to write such a function:

var counter = 0;

function increment() {
  counter = counter + 1;

  console.log("Number of events: " + counter);
}

Multiple counters

The above code is pretty straightforward. However, we quickly run into an issue if we want to introduce a second counter. Of course, we can implement two separate counter mechanisms, like in the following example, but it is obvious that something can be improved:

var counter1 = 0;

function incrementCounter1() {
  counter1 = counter1 + 1;

  console.log("Number of events: " + counter1);
}

var counter2 = 0;

function incrementCounter2() {
  counter2 = counter2 + 1;

  console.log("Number of events: " + counter2);
}

incrementCounter1();  // Number of events: 1
incrementCounter2();  // Number of events: 1
incrementCounter1();  // Number of events: 2

The above code screams of unnecessary duplication. Obviously, this solution won’t scale to more than two or three counters. We need to figure out something better.

Introducing our first closure

What we would really like in the above example, is to somehow introduce new counters, bundled with a function that can increase them without duplicating lots of code. Here is such an attempt using closures:

function createCounter() {
  var counter = 0;

  function increment() {
    counter = counter + 1;

    console.log("Number of events: " + counter);
  }

  return increment;
}

Let’s see how this works. We will create two counters, and use them to track two independent events:

var counter1 = createCounter();
var counter2 = createCounter();

counter1(); // Number of events: 1
counter1(); // Number of events: 2

counter2(); // Number of events: 1

counter1(); // Number of events: 3

Ugh, that looks complicated… However, it is actually really simple. We just need to separate the implementation logic into small, easily digestible chunks. Let’s see what our implementation does:

  • First, it creates a local variable named counter.

  • Secondly, it creates a local function named increment that can increment the counter variable. This can be pretty strange, if you have never worked with functional languages that treat functions as data. However, it is perfectly normal, and it only takes a little practice to get used to this idea.

Please notice that at this point, the implementation of createCounter() looks almost exactly like our original implementation of a counter. The only difference is the fact that it is wrapped, or enclosed, in a function’s body. Hence, these constructs are called closures.

Now comes the tricky part:

  • The last step in createCounter() returns the local increment function. Please notice, that it does not return the result of calling the function, but the function itself.

That means, when we create new counters with the snippet bellow, we are actually generating new functions.

// fancyNewCounter is a function in this scope

var fancyNewCounter = createCounter();

This is were the power of closures lie. Every function generated with createCounter() keeps track of their own generated counter value. In a sense, the returned function remembers the environment that it was created in.

An important thing should be observed. The internal counter variables are independent of each other! For example, if we create two counters, each of them will allocate a new counter variable in the body of the closure. We can observe two things:

Each counter will start counting from number 1:

var counter1 = createCounter();
counter1(); // Number of events: 1
counter1(); // Number of events: 2

var counter2 = createCounter();
counter2(); // Number of events: 1

The second counter does not interfere with the value of the first counter:

counter1(); // Number of events: 3

Naming our counters

The message “Number of events: x” is OK, but it would be more helpful if the message could describe the type of the event we are counting. For example, it would be nice if we could add a name to our counters, like in the following example:

var catCounter = createCounter("cats");
var dogCounter = createCounter("dogs");

catCounter(); // Number of cats: 1
catCounter(); // Number of cats: 2
dogCounter(); // Number of dogs: 1

We can achieve this simply by passing an argument to our closures.

function createCounter(counterName) {
  var counter = 0;

  function increment() {
    counter = counter + 1;

    console.log("Number of " + counterName + ": " + counter);
  }

  return increment;
}

Nice! Please notice an interesting behavior in the above createCounter() implementation. Not only does it remembers the value of the local counter variable, but also the value of the passed arguments.

Improving the public interface

When I say public interface, I mean how we use the counters. It is not intuitive that the created counter is a function that will increment its value when we execute it. The following would be much simpler:

var dogCounter = createCounter("dogs");

dogCounter.increment(); // Number of dogs: 1

Let’s create such an implementation:

function createCounter(counterName) {
  var counter = 0;

  function increment() {
    counter = counter + 1;

    console.log("Number of " + counterName + ": " + counter);
  };


  return { increment : increment };
}

In the above code snippet, we simply return an object that contains all the functions in our closure. In a sense, we are defining a set of messages that our closure can respond to.

Adding a decrement

Now, we can very simply introduce a decrement function to our counter.

function createCounter(counterName) {
  var counter = 0;

  function increment() {
    counter = counter + 1;

    console.log("Number of " + counterName + ": " + counter);
  };

  function decrement() {
    counter = counter - 1;

    console.log("Number of " + counterName + ": " + counter);
  };

  return {
    increment : increment,
    decrement : decrement
  };
}

var dogsCounter = createCounter("dogs");

dogsCounter.increment(); // Number of dogs: 1
dogsCounter.increment(); // Number of dogs: 2
dogsCounter.decrement(); // Number of dogs: 1

Hidden counter actions

The above code has two redundant lines. Yes, the ones with the console.log. It would be nice to create a function explicitly for displaying the value of the counter. Let’s call such a function display.

function createCounter(counterName) {
  var counter = 0;

  function display() {
    console.log("Number of " + counterName + ": " + counter);
  }

  function increment() {
    counter = counter + 1;

    display();
  };

  function decrement() {
    counter = counter - 1;

    display();
  };

  return {
    increment : increment,
    decrement : decrement
  };
}

var dogsCounter = createCounter("dogs");

dogsCounter.increment(); // Number of dogs: 1
dogsCounter.increment(); // Number of dogs: 2
dogsCounter.decrement(); // Number of dogs: 1

It looks very similar to the increment() and decrement() functions, yet it is very different. We didn’t return it in the resulting object! That means that the following will fail:

var dogsCounter = createCounter("dogs");

dogsCounter.display(); // ERROR !!!

We made the display() function hidden from the outside world. It is only available from inside of createCounter().

Abstract data types

As you can see, we can very easily introduce abstract data types with closures. For example, let’s create an implementation for a stack using closures.

function createStack() {
  var elements = [];

  return {
    push: function(el) { elements.unshift(el); },
    pop: function() { return elements.shift(); }
  };
}

var stack = createStack();

stack.push(3);
stack.push(4);
stack.pop(); // 4

Note: Closures are probably not the best implementation for a stack data type in JavaScript. Prototypes are probably more memory friendly.

Closures and OOP

If you have done any object oriented programming in your life, you will probably notice that the above constructs look very similar to classes, objects, instance variables and private/public methods.

A closure, similarly like a class, associates some data with a couple of functions that operate on them. Consequently, you can use a closure anywhere that you might normally use an object.

Final words

Closures are an awesome property of a programming language. They can come very handy in situations when we want to create “true” hidden fields in JavaScript, or need to create very simple constructs, that would be considered an overkill for a class.

comments powered by Disqus

About Igor Šarčević

Igor is a programming enthusiast with a passion for mathematics and intelligent systems. He gets lots of joy while solving difficult problems with a handful of dedicated people. He likes to be helpful to others and tries to improve himself on a daily basis. When he is not coding you can probably find him running, reading some Japanese poetry or improving his ninja skills.

Suggested Reads

Inject is a fundamental building block

Inject is one of the fundamental, and most versatile constructs available in functional languages. It can be used to implement map, select, max, all? and a bunch of other iteration related methods. Unfortunately, many programmers are not aware of its awesome powers. This article is here to improve this fact.

Contact

Rendered Text is a software company. For questions regarding Semaphore, please visit semaphoreci.com. Otherwise, feel free to get in touch any time by sending us an email.

Rendered Text
Svetozara Miletica 10
21000 Novi Sad
Serbia