Blog>
Snippets

Memory Leak Patterns in Global Variables

Discuss common patterns that lead to memory leaks through global variables and how to avoid them by limiting the use of global state and using let and const for block-scoping.
/* Example 1: Improper cleanup of DOM elements */
var elements = [];
function createElements() {
  var newElement = document.createElement('div');
  document.body.appendChild(newElement);
  // Holding a reference in a global variable leads to a memory leak if not cleaned up properly
  elements.push(newElement);
}
function cleanUp() {
  // This cleanup function is an attempt to release memory
  for (var i = 0; i < elements.length; i++) {
    var el = elements[i];
    if (el.parentNode) {
      el.parentNode.removeChild(el);
    }
  }
  // However, we also need to clear the array to avoid memory leaks
  elements = [];
}
This example shows a common pattern leading to memory leaks, where DOM elements are stored in a global array but not properly cleaned up. The cleanUp function is removing elements from the DOM, but we must also clear the global array to fully avoid the memory leak.
/* Example 2: Event listeners attached to global objects */
var myGlobalObject = {};

function attachEvent() {
  var element = document.getElementById('myElement');
  // Attaching an event listener with a reference to a global object creates a closure
  element.addEventListener('click', function() {
    console.log(myGlobalObject);
  });
}

function detachEvent() {
  var element = document.getElementById('myElement');
  // We need a named function to properly detach the event listener and avoid memory leaks
  // This is the correct way to clean up event listeners
  if (element) {
    element.removeEventListener('click', handler);
  }
}

function handler() {
  console.log(myGlobalObject);
}
This example demonstrates how failing to clean up event listeners that reference global objects can cause memory leaks. We should use named handler functions to properly detach event listeners when they are no longer needed.
/* Example 3: Not using block-scoped variables leads to unintentional global variable declaration */
function leakyFunction() {
  leakyVar = 'This variable is globally scoped because `var` keyword is missing';
  // To prevent a memory leak, always declare variables with `let` or `const`
  let safeVar = 'This variable is block-scoped, much safer!';
}
In this example, failing to use 'var', 'let', or 'const' results in the creation of an accidental global variable. Always use 'let' or 'const' for block-scoping to prevent such unintentional memory leaks.