Closures in JavaScript: A fundamental concept easily explained

  

If you've ever programmed in JavaScript, you've probably come across the term "closure". 

It's one of those concepts that, at first glance, can seem a bit enigmatic, even for experienced programmers. But fear not! 

In this article, we'll take you step by step through the discovery of closures, starting from the basics to understand what they really are and why they're so powerful.

🔗 Do you like Techelopment? Check out the site for all the details!

Step 1: JavaScript Functions - A Key Concept

Before we dive into closures, it's important to refresh a basic JavaScript concept: functions. In JavaScript, functions are "first-class citizens." What does that mean? This means they can be treated like any other value:

  • They can be assigned to variables.
  • They can be passed as arguments to other functions.
  • They can be returned from other functions.

This last point, in particular, is crucial to understanding closures.

Example:

function greet(name) {
    return "Hello, " + name + "!";
}

let myFunction = greet; // Assign the function to a variable
console.log(myFunction("Mark")); // Output: Hello, Mark!

Step 2: Scope and Variable "Context"

Each function in JavaScript creates its own "scope". The scope is basically the "area" within which the variables defined in that function are accessible. When a function is executed, it creates an execution context that includes its scope.

Let's consider this example:

function createCounter() {
    let counter = 0; // This variable lives only inside createCounter

    function increment() {
        counter++;
        console.log(counter);
    }

    increment();
    increment();
}

createCounter(); // Output: 1, 2
// console.log(counter); // Error: counter is not defined here!

In the above example, the variable counter is only accessible within the function createCounter and functions defined within it (such as increment). Once createCounter has finished executing, we would normally expect counter to be "forgotten" from memory. But this is where the magic comes in!


Step 3: The Birth of Closure - Nested Functions and Scope Persistence

Now imagine that you want to return the increment function from the createCounter function.

function createMagicCounter() {
    let counter = 0; // This variable should "die" at the end of createMagicCounter

    function increment() {
        counter++;
        return counter;
    }

    return increment; // Return the increment function
}

let myCounter = myCounter(); // Now myCounter is the increment function

console.log(myCounter()); // Output: 1
console.log(myCounter()); // Output: 2
console.log(myCounter()); // Output: 3

Here's the surprise! Even though the createMagicCounter function has finished executing, the counter variable hasn't been deleted from memory! Every time we call myCounter(), the counter variable is incremented andretains its previous state.

This phenomenon is what we call closure.


What is a Closure? The Simple but Powerful Definition:

A closure occurs when a function "remembers" and can access its lexical scope (i.e., the variables in its creation environment) even when it is executed outside that environment.

In even simpler terms:

A closure is a function that, together with its surrounding lexical environment (the variables that were in its scope when it was created), forms a unit. The function "captures" these variables and keeps them alive, even after the external function that defined them has finished executing.

In our createMagicCounter example, the increment function forms a closure with the counter variable. Even though createMagicCounter has finished, increment "remembers" counter and can still read and modify it.


Why are Closures Useful? Practical Applications

Closures are a powerful concept and have many practical applications in JavaScript:

  1. Keeping State Private: As seen in the counter example, closures allow us to create "private" variables that cannot be accessed directly from the outside, providing data encapsulation. This is essential for creating stateful forms and objects.
  2. Creating Factory Functions: We can use closures to create functions that, in turn, generate other functions with custom behavior.
    function createCustomGreet(initialGreet) {
    
    return function(name) {
    return initialGreet + ", " + nome + "!";
    };
    } let greetInItalian = createCustomGreet("Ciao");
    let greetInEnglish = createCustomGreet("Hello");
    console.log(greetInItalian("Giulia")); // Output: Ciao, Giulia!
    console.log(greetInEnglish("John")); // Output: Hello, John!
  3. Event Handling and Callbacks: In web applications, closures are often used to access variables from the surrounding context within event callback functions.
    function handleClick(elementID) {
    
    document.getElementById(elementID).addEventListener('click', function() {
    console.log("You clicked on the element with ID: " + elementID);
    });
    } // Here the closure captures 'elementID' to use in the callback function handleClick('specialButton');
  4. Currying and Functional Programming: Closures are a cornerstone of functional programming, enabling techniques like currying (turning a function with multiple arguments into a sequence of functions with a single argument).

In Conclusion

Closures are not some magical or esoteric construct, but a natural consequence of the way JavaScript handles scope and functions. Understanding closures will open the door to writing more modular, flexible, and powerful code.

Remember: the key is that a function "carries with it" the environment in which it was created. This allows it to access and interact with variables in that environment, even when the function that generated them is no longer running.

So, next time you hear about closures, don't let that scare you. Just think of a function that has special memory!



Follow me #techelopment

Official site: www.techelopment.it
facebook: Techelopment
instagram: @techelopment
X: techelopment
Bluesky: @techelopment
telegram: @techelopment_channel
whatsapp: Techelopment
youtube: @techelopment