DEV Community

Cover image for Warping realities: JavaScript closures in the Developer's Toolkit.
Numerous Oriabure
Numerous Oriabure

Posted on

Warping realities: JavaScript closures in the Developer's Toolkit.

Table of Contents:

  1. Introduction:
  • What are closures?
  • Significance of closures in JavaScript?

2 Functions recap.

3 Definition of closures in the concept of JavaScript

5 Memory management

6 Common pitfalls while using closures in JavaScript

Introduction

What exactly are closures?

Closures in layman terms simply means closing(😁). It entails the action of enclosing an entity within another.

Significance of closures in JavaScript.

  • Encapsulation: Safely bundling data and functions, reducing naming conflicts.
  • Data Privacy: Creating private variables, enhancing security.
  • Function Factories: Dynamically generating functions with specific configurations.
  • Callback Functions: Maintaining state in asynchronous operations.
  • Module Patterns: Structuring code into modules with private and public methods.

Functions recap

Before we jump into functions I want to set take you down the memory lane of functions and how they relate to closures.
Think of functions as recipes. They're sets of instructions you can use over and over again, like a cooking recipe. You can take a recipe (function), tweak it, and even pass it along to others. These recipes also create a kitchen (scope) where ingredients (variables) are kept separate and organized.
Now, closures are like hidden notes within these recipes. When you write a recipe(function) in JavaScript, it not only lists ingredients and steps but also has a secret compartment(closure). This compartment allows the recipe to remember things from the kitchen (outer function) even after it's done cooking. It's like having a cooking secret that involves not just the steps but also some insider knowledge from the kitchen. This hidden info is handy for keeping some details private, organizing things neatly, and pulling off some nifty programming tricks.

Definition of closures in the concept of JavaScript.

In JavaScript, a closure is a combination of a function and the lexical environment within which that function was declared.
The lexical environment is everything outside of the function. Take a look at the code snippet below.
function outerFunction() {
  // Outer function scope
  let outerVariable = 'I am from the outer function';

  function innerFunction() {
    // Inner function scope
    console.log(outerVariable);
  }

  innerFunction(); // Call the inner function
}

outerFunction(); // Output: I am from the outer function
Enter fullscreen mode Exit fullscreen mode

As you can observe from the snippet, the outerVariable was printed because because the function inner() could access it due to the closure property.
Those coming from languages like Python wouldn't really find this concept strange because of the syntactic sugar in those higher level languages. In languages like C, one would invaluably need the knowledge of functions and pointers to replicate this functionality.

Memory management

Although closures in JavaScript powerful and useful, they can potentially lead to memory management issues if not used carefully. Some common scenarios are outlined below:

Retaining Unnecessary References:

  • Closures can unintentionally retain references to variables from their outer scope, preventing them from being garbage collected.
  • If a closure is holding references to large objects or data structures that are no longer needed, it can lead to unnecessary memory consumption.
function createClosure() {
  let data = /* some large data */;
  return function () {
    // The closure retains a reference to 'data'
    console.log(data);
  };
}
Enter fullscreen mode Exit fullscreen mode

Accidental Global Variable Retention:

If a closure references a variable from an outer scope that happens to be a global variable, the closure will prevent that global variable from being garbage collected even if the closure is long-lived.

Event Handlers and DOM Nodes:

  • Closures used in event handlers can lead to memory leaks if they capture references to DOM elements.
  • When an element is removed from the DOM, the closure may still reference it, preventing the element and associated resources from being released.
function setupEvent() {
  let element = document.getElementById('someElement');
  element.addEventListener('click', function () {
    // The closure retains a reference to 'element'
    console.log('Element clicked');
  });
}
Enter fullscreen mode Exit fullscreen mode

Common pitfalls while using closures in JavaScript.

Performance Concerns:

  • Closures can have performance implications, especially in situations where they are created in large quantities or are long-lived. Excessive use of closures may lead to increased memory consumption and slower performance.
function processArray(array) {
  let result = [];

  for (let i = 0; i < array.length; i++) {
    result.push(function () {
      console.log(array[i]);
    });
  }

  return result;
}
Enter fullscreen mode Exit fullscreen mode

In this example, the closures in the array capture the loop variable, and they will all reference the same i value when invoked.

Memory Leaks with Event Handlers:

  • Closures used in event handlers can lead to memory leaks if not handled carefully. If an element is removed from the DOM, but the closure still references it, the element and its associated resources may not be released.
function setupEvent() {
  let element = document.getElementById('someElement');
  element.addEventListener('click', function () {
    // The closure retains a reference to 'element'
    console.log('Element clicked');
  });
}
Enter fullscreen mode Exit fullscreen mode

To avoid memory leaks, remember to remove event listeners when they are no longer needed.

Accidental Shared Variables:

  • If you use closures in a loop to create functions, be cautious about capturing loop variables. If not done correctly, all the closures may end up sharing the same variable.
for (var i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(i); // Outputs 5, 5, 5, 5, 5
  }, 1000);
}
Enter fullscreen mode Exit fullscreen mode

To avoid this, you can use let in modern JavaScript versions or create a new scope for each iteration using an IIFE (Immediately Invoked Function Expression).

for (let i = 0; i < 5; i++) {
  setTimeout(function () {
    console.log(i); // Outputs 0, 1, 2, 3, 4
  }, 1000);
}
Enter fullscreen mode Exit fullscreen mode

Summary

So far we've learnt about closures in JavaScript. Why they're used and how to use them. We also looked into different scenarios in which their usage might be abused. JavaScript can be a fun and powerful languages at times. But that could turn the other way around if abused or used wrongly. I hope I've been able to clarify your thoughts on closures in JavaScript. I'll leave some links down below incase you want to go deeper into the concept. For now Bye😁❀.

Please like and comment for more of these kind of articles.
P.S This is my first😍😊.

Thank you.

MDN Docs
Eloquent JavaScript by Marijn Haverbeke

Top comments (0)