Los closures (clausuras) son uno de los conceptos más poderosos y elegantes de JavaScript. Una vez que los entiendas, podrás escribir código más limpio, crear patrones avanzados y entender mejor cómo funciona JavaScript internamente.
Un closure (clausura) es cuando una función "recuerda" las variables de su scope padre, incluso después de que esa función padre haya terminado de ejecutarse.
En otras palabras, un closure te permite acceder al scope de una función externa desde una función interna. Los closures se crean cada vez que se define una función.
Pero lo mejor es que lo veamos con un ejemplo:
function crearContador() {
let cuenta = 0 // Variable en el scope padre
return function() {
cuenta++ // La función interna "recuerda" cuenta
return cuenta
}
}
const contador = crearContador()
console.log(contador()) // 1
console.log(contador()) // 2
console.log(contador()) // 3
// ¡La variable 'cuenta' sigue existiendo!
crearContador() crea una variable cuentacrearContador() termina, la función devuelta mantiene acceso a cuentacontador(), modifica la misma variable cuenta┌─────────────────────────────────────────────────┐
│ FUNCIÓN EXTERNA: crearContador() │
│ │
│ let cuenta = 0 ← Esta variable queda │
│ "capturada" por el closure │
│ │
│ ┌───────────────────────────────────────────┐ │
│ │ FUNCIÓN INTERNA │ │
│ │ │ │
│ │ return function() { │ │
│ │ cuenta++ ← Accede a 'cuenta' │ │
│ │ return cuenta │ │
│ │ } │ │
│ │ │ │
│ └───────────────────────────────────────────┘ │
│ │
│ La función interna "recuerda" la variable │
│ 'cuenta' incluso cuando crearContador() │
│ ya ha terminado de ejecutarse │
│ │
└─────────────────────────────────────────────────┘
function crearContador() {
let cuenta = 0
return function() {
cuenta++
return cuenta
}
}
const contador1 = crearContador()
const contador2 = crearContador()
// contador1 y contador2 son independientes
// y cada uno tiene su propia copia de la variable "cuenta"
console.log(contador1()) // 1
console.log(contador1()) // 2
console.log(contador2()) // 1 (independiente)
console.log(contador1()) // 3
console.log(contador2()) // 2
Cada contador tiene su propia copia de la variable cuenta. ¡Esto es la magia de los closures!
No sólo pasa con las variables dentro de la función, también podemos acceder a los parámetros de la función padre.
function crearSaludador(saludo) {
return function(nombre) {
return saludo + ', ' + nombre + '!'
}
}
const saludarEnEspanol = crearSaludador('Hola')
const saludarEnIngles = crearSaludador('Hello')
const saludarEnFrances = crearSaludador('Bonjour')
console.log(saludarEnEspanol('Ana')) // "Hola, Ana!"
console.log(saludarEnIngles('John')) // "Hello, John!"
console.log(saludarEnFrances('Marie')) // "Bonjour, Marie!"
Una utilidad de los closures es crear datos privados. De forma que no se pueda acceder a ellos desde fuera de la función, pero sí desde dentro.
Así podemos devolver un objeto con métodos que nos permitan acceder a los datos privados y asegurarnos de que no se puedan modificar desde fuera.
function crearPersona(nombreInicial) {
let nombre = nombreInicial // Variable "privada"
let edad = 0
return {
getNombre: function() {
return nombre
},
setNombre: function(nuevoNombre) {
nombre = nuevoNombre
},
getEdad: function() {
return edad
},
cumplirAnos: function() {
edad++
return edad
}
}
}
const persona = crearPersona('Juan')
console.log(persona.getNombre()) // "Juan"
persona.setNombre('Carlos')
console.log(persona.getNombre()) // "Carlos"
console.log(persona.cumplirAnos()) // 1
// No podemos acceder directamente a las variables privadas
console.log(persona.nombre) // undefined
console.log(persona.edad) // undefined
No hay que confundir los closures con las variables globales. Son dos conceptos muy distintos y, de hecho, las closures pueden ser una solución para evitar el uso de variables globales y algunos problemas que pueden surgir con ellas:
// ❌ Variables globales - pueden ser modificadas por cualquiera
let contador = 0
function incrementar() {
contador++
return contador
}
function decrementar() {
contador--
return contador
}
// Problema: cualquier código puede modificar 'contador'
contador = 1000 // ¡Ups! Se rompió nuestro contador
// ✅ Closure - datos protegidos
function crearContador() {
let contador = 0 // Variable privada
return {
incrementar: function() {
contador++
return contador
},
decrementar: function() {
contador--
return contador
},
obtenerValor: function() {
return contador
}
}
}
const miContador = crearContador()
console.log(miContador.incrementar()) // 1
console.log(miContador.incrementar()) // 2
console.log(miContador.decrementar()) // 1
// No podemos acceder directamente a 'contador'
// miContador.contador = 1000 // No funciona
Inicia sesión
Para guardar tu progreso y desbloquear logros