Understanding Closures

· 2 min read

Recently while organizing JavaScript fundamentals, I found the concept of closure wasn’t very clear, so I studied it for a while. As they say, “leaving footprints in snow,” I’m documenting this here.

What is a Closure

A function bundled together with references to its surrounding state (lexical environment) forms a closure. In other words, closures give 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.

From MDN

Notes

  1. Function scope, not block scope
  2. Closures are stateful functions

Let’s start with some examples to see which is a closure

Example 1

   function init() {
        var name = 'Mozilla'; // name is a local variable created by init
        function displayName() {
          // displayName() is the inner function
          alert(name); // uses variable declared in parent function
        }
        displayName();
      }
      init();

Example 2

function makeFunc() {
    var name = "Mozilla";
    function displayName() {
        alert(name);
    }
    return displayName;
}
var myFunc = makeFunc();
myFunc();

TIP

Note that Chrome’s debugging state clearly shows which is a closure. This helps with understanding.

Through these examples, we can actually say:

A closure is a function object that can access variables in another function's scope, and you can also say a closure is a stateful function.

Using Closures

Now that we know what closures are, let’s see what they can do.

  1. Encapsulating private variables
  2. Modules
  3. Using in block scope
  4. Event listeners

We may consciously or unconsciously use these patterns, perhaps without realizing it.

Closures are a Technique, Not Language-Specific

As mentioned above, closure is a technical approach that’s language-independent. So does Java have closures? Yes, it does.

Closures in Java

For example, in lambda expressions, we pass in external function variables, forming closures.

int factor = 3;

numbers.stream()
  .filter(e -> e % 2 == 0)
  .map(e -> e * factor)
  .collect(toList());

Why is this a closure? Note that factor is from the outer function’s lexical environment.

Conceptual Confusion - Lambda, Anonymous Functions, Closures

  1. Lambda is an anonymous function, just different terminology
  2. Closures are different from anonymous functions. In the above example, if we remove factor, it’s no longer a closure

Practice Problems

After understanding closures, let’s look at some more complex problems.

Problem 1

function fun(n, o) { 
  console.log(o);
  return { 
    fun: function(m) { // ③
           debugger;
      return fun(m, n); // ④ 
    }
  };
}


// First example
var a = fun(0); // returns undefined
a.fun(1); // returns 0
a.fun(2); // returns 0
a.fun(3); // returns 0

// Second example
var b = fun(0)
  .fun(1)
  .fun(2)
  .fun(3); // undefined,0,1,2

// Third example
var c = fun(0).fun(1);
c.fun(2);
c.fun(3); // undefined,0,1,1

Problem 2

   var myObject = {
        foo: 'bar',
        func: function () {
          var self = this;
          console.log('outer func:  this.foo = ' + this.foo);  // bar
          console.log('outer func:  self.foo = ' + self.foo);   // bar
          (function () {
            console.log('inner func:  this.foo = ' + this.foo); // undefined
            console.log('inner func:  self.foo = ' + self.foo); // bar
          })();
        },
      };

      myObject.func();

  • This problem theoretically tests the this binding concept. I remember reading a book that had a great line: “this always points to the object that calls it.” Here, that’s myObject.
  • func and its context

Final Thoughts

The programming world values simplicity and elegance as beauty. Often, if you find a concept very complex, you might be misunderstanding it. I deeply agree with this statement. Approaching concepts like closures with this mindset should make them simpler.

References