Fugas de memoria y addEventListener

El método removeEventListener


Las fugas de memoria son un tema importante que afecta al rendimiento de un sitio web y sus usuarios al utilizarlo, y se basa en cómo manejas los eventos y como está construído el código de tu página.

¿Qué es una fuga de memoria?

Al utilizar variables y objetos de nuestro código, estos se cargan en memoria y se utilizan cuando es necesario. Sin embargo, muchas veces dejan de ser necesarios y lo ideal sería que se eliminaran de memoria para no ocuparla sin necesidad, por ejemplo, con el Garbage Collector de Javascript, un sistema automático que busca los objetos o variables que ya no se necesitan para eliminarlos de memoria.

La fuga de memoria se produce cuando resulta imposible para el Garbage Collector eliminar de memoria un dato u objeto porque se encuentra referenciado y asume que puede volver a ser utilizado. Esto hace que la página continue cargando y cargando datos en memoria, consumiendo más y más memoria.

Las fugas en .addEventListener()

El problema de las fugas de memoria en los addEventListener() se produce cuando se añade un listener a un elemento del DOM y luego, ese elemento del DOM se elimina de la página sin haber eliminado el listener con removeEventListener().

Lo que ocurre es que el listener sigue referenciado al elemento del DOM y por lo tanto es imposible para el Garbage Collector eliminarlo de memoria. Esto se puede complicar si estamos en un bucle o si se añaden y eliminan elementos al DOM continuamente, ya que se estarían acumulando listeners que no se pueden eliminar, consumiendo cada vez más memoria.

El método .removeEventListener()

El método .removeEventListener(), como su propio nombre indica, sirve para eliminar un listener que se ha añadido previamente al elemento con .addEventListener():

MétodoDescripción
.removeEventListener(event,func)Elimina la funcionalidad func asociada al evento event.

Veamos un ejemplo, donde añadimos dos listener click que hacen cada uno una función: action y toggle. Luego, retiramos uno de ellos (action) con .removeEventListener(). En este ejemplo sólo debería actuar la funcionalidad toggle:

const button = document.querySelector("button");

const action = () => alert("Hello!");
const toggle = () => button.classList.toggle("red");

button.addEventListener("click", action);         // Añade listener de saludar
button.addEventListener("click", toggle);         // Añade listener de color
button.removeEventListener("click", action);      // Elimina listener de saludar
.red { background: red }
<button>Saludar</button>

Observa que hemos utilizado la constante para referenciar a la función que queremos añadir en el listener o retirarla. Tanto en el addEventListener() como en el removeEventListener() se hace referencia a la misma función: a action.

Un error muy típico es indicar una función diferente que hace lo mismo que la primera:

button.addEventListener("click", function() { alert("Hello!"); });
button.removeEventListener("click", function() { alert("Hello!"); });

Este ejemplo no funcionaría correctamente, porque la primera función es diferente a la segunda, aunque hagan lo mismo. El método removeEventListener() se espera que se le pase exactamente la misma función.

La opción once de addEventListener()

Utilizando la opción once: true en las opciones de un addEventListener() podemos crear listeners de un sólo uso, que trabajarían de forma similar a un addEventListener() que cuando se ejecuta una vez, se elimina con un removeEventListener():

<button>Saludar sólo una vez</button>

<script>
const button = document.querySelector("button");

button.addEventListener("click", () => {
  alert("¡Hola desde LenguajeJS!");
}, { once: true });
</script>

Si pulsamos en el botón, comprobaremos que se ejecuta la función y se produce el alert. Sin embargo, si volvemos a pulsar el botón, no se volverá a ejecutar. El once determina que el listener funcionará sólo la primera vez, y luego se eliminará. Es algo interesante si tenemos funcionalidades que no queremos que ocurran más de una vez.

Errores comunes

Existen situaciones donde se producen fugas de memoria comunmente:

  • 1️⃣ Eventos globales. Cuando haces un addEventListener() en window, document o globalThis. Este listener seguirá funcionando incluso cuando cambias de página en una SPA (Single Page Application).

  • 2️⃣ Si usas un addEventListener() que no se elimina con el removeEventListener().

Por estas razones, por cada addEventListener() que utilices, piensa que debería usarse un removeEventListener() en el caso de que ya no se utilice o se elimine del DOM.

¿Quién soy yo?

Soy Manz, vivo en Tenerife (España) y soy streamer partner en Twitch y profesor. Me apasiona el universo de la programación web, el diseño y desarrollo web y la tecnología en general. Aunque soy full-stack, mi pasión es el front-end, la terminal y crear cosas divertidas y locas.

Puedes encontrar más sobre mi en Manz.dev