Blog

Front End Development topics with a touch of humor

Opening the door on closures

Elucidating this shadowy feature of the JavaScript language

October 12, 2020


Last time, we learned that a closure is the binding of a function with its lexical enviroment, which practically means that inner functions have access to the variables of outer functions, even when invoked in a different context.

I don't know about you, but reading about it doesn't help me much. Let's go over some examples and see the phenomenon in action.

              function counter() {
                  let n = 0;
                  return {
                      increment: function() { 
                          return n++;
                      },
                      decrement: function() {
                          return n--;
                      }
                  }
              }
          

We have here an outer function called counter that declares n with a value of 0, and that returns an object with two methods, one that adds to n and one that subtracts from it. Notice that the increment/decrement operators are placed before the n; otherwise the program will return the value from before the operation occurs, and I don't want that. From our earlier study, we know that the two methods will have access to n.

              const countBot = counter();
              countBot.increment();
              //1
              countBot.decrement();
              //2
          

Here we call the increment method and, as it has access to the local variable from its outer function, successfully increments and returns it. We call the decrement method and it, too, executes correctly, resulting in the value going back to 0.

This is CountBot

Just to prove that we did anything, let's increment again.

              countBot.increment();
              //1
          

What would happen if we call the parent function again, assigning the result to a new variable, and calling that?

              const countBotII = counter();
              countBotII.increment();
              //1
          

Hey, it didn't increment n... did it? What happened?

Well, countBotII was returned by counter, and thus it has access to counter's local variables, but it is a different "local" than countBotI. It has a different n. Ye Olde MDN docs illustrates this point in a slightly different way.

            function makeAdder(x) {
                return function(y) {
                  return x + y;
                };
              }
              
              var add5 = makeAdder(5);
              var add10 = makeAdder(10);
              
              console.log(add5(2));  // 7
              console.log(add10(2)); // 12
              [from the MDN docs]
          

Here we have an outer function makeAdder with an argument, x, that returns a function that accesses x as a local argument and adds its own argument, y, to that. The inner function will remember x, then, when it's called in the global space. In the example, both instances of makeAdder, one with an argument of 5 and the other of 10, remember those arguments then they are later called. They remember the arguments because of the closure.