Imagine we wanted to make it easy to attach a counter to any function. We want to pass a function foo to our API and it’ll return a new function bar that:

  • calls foo
  • logs how many time bar was called

In Javascript, we can do this with closures!

function with_counter(callback) {
    let counter = 0;
    return () => {
        callback();
        ++counter;
        console.log(`Function was now called ${counter} time${counter > 1 ? 's' : ''}!`);
    }
}
 
const bark = () => {
    console.log("몽몽!");
}
 
const bark_with_counter = with_counter(bark);
bark_with_counter(); // 몽몽!\nFunction was now called 1 time!
bark_with_counter(); // 몽몽!\nFunction was now called 2 times!
bark_with_counter(); // 몽몽!\nFunction was now called 3 times!

If you come from a traditional programming background, you might wonder why this even works. Indeed, counter is defined inside of with_counter(), so naturally when it stops executing and the program leaves its scope, counter should disappear. But nope! As per the docs, closures are functions that are bundled together with references to their surrounding state. If you think about it, this means that every variable defined in javascript is global. You just need a way to access it. Our closure function, in our case, is our way to access it.

Here’s a non-trivial example, straight from GreatFrontend. Assume we wanted to code up an elevator’s ā€œopen buttonā€: whenever a user pushes it, the elevator stays open for n seconds, and this timer resets every time a user pushes the open button again. This technique is called debouncing.

Here’s a possible solution:

export default function debounce(func, wait) {
  let lastTimeoutId = 0;
  return function(...args) {
    clearTimeout(lastTimeoutId); // if id doesn't exist, function does nothing
    lastTimeoutId = setTimeout(func.bind(this, ...args), wait);
  }
}

Note the arguments passed to the function, and binding this so that the function will use this from the calling context, and not the creation context. This (pun intended) deserves a post of its own.

How does this work?

In JS (at least for Google’s V8 engine), most variables are actually stored on the heap (small values and variables not referred by anything might live in the stack but for the sake of this post, let’s assume everything is on the heap). When we define variables in a function, those are defined in a scope on JS’s scope chain. It’s simply a chain of scopes starting from the smallest one (wherever we’re using our object from) to the broadest one (the Global object). So whenever we call a function, a scope is created for that particular function. What’s interesting is that scopes are never really destroyed UNLESS the garbage collector deems that no other objects is referring to anything in that scope, and therefore, can be safely free’d.

This is pretty cool! We defined a number counter in the with_counter() scope that was created when we called it for the first time. When with_counter created our function, and returned it to us, although with_counter stopped executing, its scope was never destroyed, because our bark_with_counter function has a reference to counter!

This is simple, it’s how garbage collection has always worked, but the fact that V8 stores simple values on the heap (or moves them from stack to heap) when an object references them is new to me, and I thought was interesting enough to write about.

References

https://www.dmitryfrank.com/articles/js_closures https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Closures