Cierres en JavaScript: Mecanismo del Entorno Léxico y Aplicaciones Prácticas
Un cierre en JavaScript es una combinación de una función y su entorno léxico, donde una función interna mantiene acceso a variables de un ámbito externo incluso después de que la función externa haya terminado su ejecución. Esto permite la encapsulación de estado y un comportamiento predecible en contextos asíncronos.
El concepto se originó en el cálculo lambda de Alonzo Church. Las funciones de orden superior devuelven o aceptan otras funciones, capturando variables libres—aquellas usadas dentro pero declaradas fuera.
Un ejemplo básico de contador demuestra el principio:
function counter() {
let count = 0
return function(step) {
count = count + step
return count
}
}
const myCounter = counter()
console.log(myCounter(1)) // 1
console.log(myCounter(1)) // 2
console.log(myCounter(1)) // 3
La función interna se cierra sobre count, evitando la recolección de basura. Esta es una referencia viva: los cambios se reflejan en tiempo real.
Cierres como Análogo de POO
Los cierres emulan campos privados y APIs públicas de objetos. Un contador con métodos:
function Counter(initialValue = 0) {
let count = initialValue
return {
increment: function(step = 1) {
return count += step
},
decrement: function(step = 1) {
return count -= step
},
getValue: function() {
return count
},
reset: function() {
return count = initialValue
}
}
}
const myCounter = Counter(10)
console.log(myCounter.getValue()) // 10
console.log(myCounter.increment(5)) // 15
Un equivalente usando clases con campos privados (#count) muestra lógica idéntica. En almacenes Pinia/Vuex, los cierres encapsulan estado reactivo:
export const useCounterStore = defineStore('counter', () => {
const count = ref(0)
const initValue = 0
function increment(step) {
count.value += step
}
return { count, increment }
})
Matices en Bucles e Iteradores
El comportamiento de los cierres en bucles depende del hoisting y el ámbito de bloque.
Con var—una variable compartida para todas las iteraciones:
for (var i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i) // 3, 3, 3
}, 100)
}
Con let—un entorno léxico separado por iteración:
for (let i = 0; i < 3; i++) {
setTimeout(function() {
console.log(i) // 0, 1, 2
}, 100)
}
Iteradores como forEach, map, filter crean cierres automáticamente:
[0, 1, 2].forEach(function(i) {
setTimeout(function() {
console.log(i) // 0, 1, 2
}, 100)
})
Práctica: Callbacks y Eventos
En código asíncrono, los cierres resuelven problemas de contexto.
Ajax con un ID único:
function ajaxRequest(url) {
let requestId = Math.random().toString(36).substring(2)
fetch(url).then(data => {
console.log(`Request ${requestId} completed:`, data)
}).catch(error => {
console.log(`Request ${requestId} failed:`, error.message)
})
}
Manejadores de eventos:
function clickSetup(elemId, message) {
const elem = document.getElementById(elemId)
elem.addEventListener('click', function() {
console.log(message)
})
}
Las funciones flecha funcionan de manera idéntica a las regulares.
Optimización y Depuración
Los motores JS (V8) excluyen variables no usadas de los cierres para ahorrar memoria. En DevTools (Scope → Closure), solo las variables relevantes son visibles.
Prácticas clave:
- Usa
let/consten bucles para ámbito de bloque. - Prefiere cierres para estado privado sin clases.
- En callbacks asíncronos, fija el contexto temprano.
- Monitorea la memoria en cierres de larga duración.
Conclusiones Clave
- Cierre = función + entorno léxico con referencias vivas a variables.
leten bucles crea cierres por iteración,varno.- Emula campos privados de POO, usado en almacenes Vuex/Pinia.
- Optimizado por motores: las variables no usadas se excluyen.
- Esencial para callbacks asíncronos y eventos.
— Editorial Team
Aún no hay comentarios.