Practical Applications Of Closure

Closure isn't just a technical curiosity. It's the engine behind helper functions, design patterns, and asynchronous JavaScript.

April 25, 20263 min read3 / 3

As we've seen, closure gives our functions two types of memory. They get a temporary memory when they run (the execution context), but they also get a persistent private memory that sticks around. This is a vital part of the toolkit for writing professional code.

The Essentials

  1. Utility Functions: Patterns like once and memoize that change function behavior based on history.
  2. Module Pattern: Storing persistent labels that don't pollute the global namespace.
  3. Asynchronicity: Ensuring callbacks still have their data when they return from a server or API.

Pattern 1: Helper Functions (once & memoize)

We can "oncify" a function to prevent it from running more than once. This is useful for things like a "win" function in a game or a payment trigger. The function checks a boolean in its backpack to see if it has run before.

Memoization uses the backpack as a cache. If I have a function nthPrime calculating the 1.2 millionth prime number, I don't want to redo that math every time. I check the backpack: is the result for 1.2 million already there? If so, return it instantly.

Pattern 2: The Module Pattern

In a 3 million line codebase, you don't want to worry that your colleague from 14 years ago used the label result in global memory. We want persistent labels that don't pollute the global namespace.

By using closure, we can have lifetime memories that aren't global. We modularize our code into persistent, private stores that are only accessible by calling the specific functions we've returned.

Pattern 3: Asynchronous Callbacks

JavaScript's popularity comes from the web browser. We speak to the internet, we fetch TikTok videos or Spotify tracks, and we want to run code on them when they return.

But that code might need other data besides what came back from the API. How is that data still available?

JavaScript
// A simplified async example function getVideos(category) { return function(data) { console.log(`Displaying ${category} videos:`, data); }; } const showTikToks = getVideos('Viral'); // ... much later, after API call returns ... showTikToks(apiResult);

The showTikToks function holds onto its "Viral" category in its backpack. Even if it runs hours later, that data is still available because of closure.

Pattern 4: Iterators & Generators

Iterators allow us to go through a list of data without a for loop. Each time we run the function, it returns the next element. Since functions don't normally remember where they were, iterators use the backpack to track their position.

Generators go even further: they allow us to "pause" a function halfway through until some async work returns. We hold onto the local memory and the line position in the code right there in the backpack.

Summary

There is almost nothing used more profoundly across JavaScript than the ability for a function to have a backpack of persistent data. It is what makes our code modular, efficient, and capable of handling complex asynchronous flows.

Now that we've mastered the foundational mechanics of memory and scope, we're ready to tackle the "dark side" of JavaScript.

In the next chapter, we'll dive into Type Coercion and Metaprogramming.

Further Reading and Watching