Closures en JavaScript: Qué son y por qué todos los usan
Los closures son una de las características más elegantes y poderosas de JavaScript 🧠. Permiten que las funciones “recuerden” el entorno en el que fueron creadas, incluso después de que ese contexto haya desaparecido. En otras palabras: una función puede acceder a variables externas aunque su ámbito original ya no exista.
🔍 ¿Qué es un closure exactamente?
Un closure ocurre cuando una función interna “cierra” sobre las variables de su función externa. Esto le permite mantener acceso a ellas incluso después de que la función externa haya terminado de ejecutarse.
function crearContador() {
let cuenta = 0;
return function () {
cuenta++;
console.log(`Contador: ${cuenta}`);
};
}
const contador = crearContador();
contador(); // Contador: 1
contador(); // Contador: 2
contador(); // Contador: 3
👉 La variable cuenta no se borra cuando crearContador() termina.
La función interna sigue “recordándola”. Ese es el closure en acción.
🧩 Cómo funciona paso a paso
- Se ejecuta
crearContador(), creando una variable localcuenta = 0. crearContador()devuelve una nueva función que usacuenta.- Aunque
crearContador()ya terminó, la función devuelta mantiene una referencia viva al entorno donde fue creada.
📦 En resumen: un closure es una función + su entorno léxico (las variables que estaban a su alcance cuando se creó).
⚙️ Otro ejemplo clásico: funciones personalizadas
function saludar(nombre) {
return function(saludo) {
console.log(`${saludo}, ${nombre}!`);
};
}
const saludaFelipe = saludar('Felipe');
const saludaMarta = saludar('Marta');
saludaFelipe('Hola'); // Hola, Felipe!
saludaMarta('Buenos días'); // Buenos días, Marta!
Aquí cada función conserva su propio contexto.
nombre no se pierde porque está dentro del closure.
🧠 Por qué los closures son útiles
Los closures se usan todo el tiempo, incluso si no te das cuenta. Veamos algunos usos comunes:
| Uso | Descripción | Ejemplo |
|---|---|---|
| Encapsulación | Permiten crear variables privadas que no son accesibles desde fuera. | Contadores, módulos, control de acceso. |
| Callbacks | Retienen el estado entre ejecuciones asíncronas. | Eventos, setTimeout, fetch. |
| Currying | Preconfiguran funciones para ser reutilizadas con distintos argumentos. | const sumar5 = x => sumar(5, x); |
🔒 Encapsulación y variables privadas
Antes de class y #private, los closures eran la forma más popular de proteger variables.
function crearBanco(saldoInicial) {
let saldo = saldoInicial;
return {
depositar(cantidad) {
saldo += cantidad;
console.log(`Depositaste $${cantidad}. Saldo: $${saldo}`);
},
retirar(cantidad) {
if (cantidad > saldo) {
console.log('Fondos insuficientes 🚫');
return;
}
saldo -= cantidad;
console.log(`Retiraste $${cantidad}. Saldo: $${saldo}`);
}
};
}
const miCuenta = crearBanco(100);
miCuenta.depositar(50); // Depositaste $50. Saldo: $150
miCuenta.retirar(70); // Retiraste $70. Saldo: $80
console.log(miCuenta.saldo); // ❌ undefined
💡 saldo está “protegido” dentro del closure — solo se puede acceder a él a través de las funciones devueltas.
⚡ Closures y setTimeout
Un caso clásico de uso (y confusión) con closures ocurre en bucles:
for (var i = 1; i <= 3; i++) {
setTimeout(() => console.log(i), 1000);
}
// Resultado: 4, 4, 4 ❌
Esto pasa porque var no tiene ámbito de bloque, y todas las funciones dentro del bucle comparten el mismo i.
✅ Solución con let o closure explícito:
for (var i = 1; i <= 3; i++) {
(function(x) {
setTimeout(() => console.log(x), 1000);
})(i);
}
// Resultado: 1, 2, 3 ✅
🧩 Buenas prácticas con closures
✅ Úsalos para encapsular datos sin recurrir a clases. ✅ Evita abusar de ellos en estructuras muy anidadas (puede dificultar la lectura). ✅ Recuerda que mantener referencias vivas puede generar fugas de memoria si no se limpian correctamente.
🎯 Conclusión
Los closures son uno de los pilares del lenguaje. Entenderlos te permite dominar conceptos más avanzados como módulos, funciones puras, currying y patrones funcionales.
Un closure no solo recuerda variables… también recuerda tu nivel de JavaScript 😉