Mastering Closures: Tips and Tricks for Better JavaScript Development
Tags: webdev, javascript, beginners, programming
Published: 2024-05-07 13:20:23

Today we will take a closer look to a common struggle for many developers starting with JavaScript (including myself!), the CLOSURES. 

Closures are one VERY powerful feature of JavaScript. They allow developers to create private variables, preserve state, and enable functional programming paradigms. 

Closures can be tricky, especially for beginners. In this post, we'll see five tips and tricks, to help you master them and become a better JS developer!


## Tip 1. Understand the Scope Chain

Your first building block to understand closures will be understanding _the scope chain_.  In order to understand scope chain, every time you need to know which variables are in scope and whenever you encounter a closure, how to access the variables from there. 

Let's break this down with an example:

```
function outerFunction() {
    let outerVariable = 'I am from outer function';

    function innerFunction() {
        console.log(outerVariable); // Accessing outerVariable from the outer function's scope
    }

    return innerFunction;
}

const closureExample = outerFunction();
closureExample(); // Output: I am from outer function

```

The scope chain in JavaScript specifies how JavaScript looks for variables. When innerFunction tries to access outerVariable, JavaScript goes upward in the order trying to find this variable. It will first check if it is available in the innerFunction scope. Since outerVariable is not lying in innerFunction; Scope is continued to check in the scope of outerFunction.

## Tip 2. Use Closures to Create Private Variables

Did you know that you could create private variables with closures? They will only be available in the closure they have been declared and not outside the global scope.

```
function createCounter() {
    let count = 0; // Private variable

    return {
        increment: function() {
            count++; // Accessing and modifying the private variable
            console.log("Count:", count);
        },
        decrement: function() {
            count--; // Accessing and modifying the private variable
            console.log("Count:", count);
        },
        getCount: function() {
            return count; // Accessing the private variable without modifying it
        }
    };
}

const counter = createCounter();
counter.increment(); // Output: Count: 1
counter.increment(); // Output: Count: 2
counter.decrement(); // Output: Count: 1

console.log(counter.count); // Output: undefined (count 

```

Private variables can create encapsulated code, where the implementation details are hidden from the outside world. 

## Tip 3. Avoid Memory Leaks

Closures can result in memory leaks. This seems to be the case for one of two reasons:

- **Retaining references:** Closures keep references to variables in their enclosing scope, preventing JavaScript's garbage collector from reclaiming memory associated with those variables even if they're no longer needed.

- **Circular references:** Closures may unintentionally form circular references, where an object references itself or another object that eventually references back to it. This prevents both objects from being garbage collected even if they're no longer in use.

Therefore, don’t forget to clean up after your closures. Nullify any references. Don’t create circular links.

## Tip 4. Use Closures for Callbacks and Event Handlers

Closures are often used for callbacks and event handlers, where you need to preserve the state between function calls. 

The best example would be when you would like to track the number the button has been pressed. See the following example:

```
function addButtonClickHandler() {
    let clickCount = 0; // State variable preserved by closure
    
    const button = document.getElementById('myButton');
    button.addEventListener('click', function() {
        clickCount++;
        console.log(`Button clicked ${clickCount} times`);
    });
}

addButtonClickHandler();

```
## Tip 5. Experiment with Functional Programming

Closures are a handy tool that can help you if you want to dive into functional programming. They enable you to use concepts such as immutability, pure functions or higher-order functions. If you don't know what are these: 

- **Immutability:** This concept highlights that once a value is assigned, it cannot be changed. Closures help enforce immutability by encapsulating variables within their scope, preventing external modification. 

- **Pure functions:** Pure functions are these functions which have no side effects and always return the same output for a given input. Closures help the creation of pure functions by preserving the state needed for computation within their lexical scope. 

- **Higher-order functions:** These can take functions as arguments and/or return functions as results. Closures enable the implementation of higher-order functions by allowing functions to be passed as arguments and captured within other functions' lexical scopes. 

## Conclusion

In conclusion, closures are a powerful feature of JavaScript that can help you write better code. By understanding the scope chain, using closures to create private variables, avoiding memory leaks, using closures for callbacks and event handlers, and experimenting with functional programming, you can take your JavaScript skills to the next level.