Closures en JavaScript: ejemplos reales que todo dev debería conocer

Los closures son uno de esos conceptos que aparecen en casi cualquier charla sobre JavaScript, pero que a veces se quedan en la teoría. En este artículo vamos a ver cuatro ejemplos reales donde los closures hacen la diferencia en el día a día de un desarrollador frontend.
1. Repaso rápido: ¿qué es un closure?
Un closure se crea cuando una función interna (inner function) captura variables del entorno de la función externa (outer function) aunque ésta haya terminado su ejecución. En otras palabras, la función interna mantiene una referencia al scope de su contenedor.
function crearContador(inicial) {
let cuenta = inicial;
return function() {
cuenta += 1; // <-- accede a "cuenta" del scope externo
return cuenta;
};
}
const next = crearContador(0);
console.log(next()); // 1
console.log(next()); // 2
Ese next sigue teniendo acceso a cuenta aunque crearContador ya haya finalizado.
2. Closures y manejadores de eventos
En una SPA es muy frecuente añadir listeners que necesiten información del momento en que se crean. Un closure nos permite “recordar” esa información sin recurrir a variables globales.
function attachHoverTooltip(element, mensaje) {
element.addEventListener('mouseenter', function() {
const tooltip = document.createElement('div');
tooltip.className = 'tooltip';
tooltip.textContent = mensaje; // "mensaje" proviene del closure
document.body.appendChild(tooltip);
const rect = element.getBoundingClientRect();
tooltip.style.top = `${rect.bottom + 5}px`;
tooltip.style.left = `${rect.left}px`;
});
element.addEventListener('mouseleave', function() {
const tooltip = document.querySelector('.tooltip');
if (tooltip) tooltip.remove();
});
}
// Uso
const btn = document.querySelector('#saveBtn');
attachHoverTooltip(btn, 'Guarda los cambios en el servidor');
El mensaje se “cierra” dentro de los callbacks del mouse, de modo que cada botón puede tener su propio texto sin sobrescribirlo.
3. Closures en operaciones asíncronas
Cuando trabajamos con fetch o setTimeout, a menudo necesitamos conservar datos entre la llamada y la respuesta. Un closure nos ahorra crear objetos auxiliares.
function cargarUsuario(id) {
fetch(`/api/usuarios/${id}`)
.then(res => res.json())
.then(function(datos) {
// "id" sigue disponible aquí
console.log(`Usuario ${id}:`, datos);
})
.catch(console.error);
}
// Llamamos varias veces con IDs diferentes
cargarUsuario(12);
cargarUsuario(47);
El callback del .then mantiene una referencia al id original, lo que es muy útil para depurar o para actualizar la UI de forma específica.
4. Encapsular estado sin clases
En proyectos donde no queremos introducir una clase completa, podemos usar un closure para crear un módulo de estado privado.
const useTema = (function() {
let tema = 'light'; // estado privado
return function(nuevoTema) {
if (nuevoTema) tema = nuevoTema;
return tema;
};
})();
// Cambiar tema
useTema('dark');
console.log(useTema()); // dark
Esta técnica es similar a lo que hacen los hooks de React, pero funciona en cualquier contexto JavaScript.
5. Pitfalls: cuándo un closure puede ser un problema
- Referencias innecesarias: si guardamos una función que captura grandes objetos, esos objetos no podrán ser recolectados por el GC, provocando memory leaks.
- Loops y closures: en bucles
forclásicos, los closures pueden capturar la misma variable y dar resultados inesperados. La solución típica es usarleto crear una IIFE.
for (var i = 0; i < 3; i++) {
setTimeout(function() { console.log(i); }, 100);
}
// → 3, 3, 3 (i es la misma referencia)
// Solución con let
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100);
}
// → 0, 1, 2
En resumen, los closures son una herramienta poderosa para escribir código más limpio y modular, siempre que los usemos con conciencia del ciclo de vida de los datos que capturamos.
Si todavía tienes dudas, prueba a crear pequeños experimentos en la consola del navegador: escribe una función que devuelva otra y observa cómo se comportan las variables. Verás que, una vez entendido, los closures dejan de ser un misterio y se convierten en tu aliado cotidiano.