Volver a JavaScript Intermedio
JavaScript
JavaScript Intermedio
Closures y Scope
Entender cómo JavaScript gestiona la visibilidad y memoria de variables es clave para escribir código predecible y sin errores.
Tipos de Scope
// Scope global
const APP_NAME = 'MiApp';
function exterior() {
// Scope de función
const varExterior = 'Soy de exterior';
function interior() {
// Scope de función (interior)
const varInterior = 'Soy de interior';
console.log(varExterior); // ✅ interior accede a exterior
console.log(APP_NAME); // ✅ interior accede al global
}
// console.log(varInterior); // ❌ ReferenceError
}
{
// Block scope — let y const son de bloque
let blockLet = 'bloque';
var blockVar = 'función';
}
// console.log(blockLet); // ❌ ReferenceError
console.log(blockVar); // ✅ var se escapa del bloque
Closures
Un closure es una función que recuerda las variables de su scope exterior incluso después de que ese scope ha terminado de ejecutarse.
function crearContador(inicio = 0) {
let cuenta = inicio; // Capturada por el closure
return {
incrementar() { cuenta++; },
decrementar() { cuenta--; },
obtener() { return cuenta; },
};
}
const contador = crearContador(10);
contador.incrementar();
contador.incrementar();
console.log(contador.obtener()); // 12
// `cuenta` es privada — no accesible desde fuera
Patrones Prácticos con Closures
// Memoización
function memoizar(fn) {
const caché = new Map();
return function (...args) {
const clave = JSON.stringify(args);
if (caché.has(clave)) return caché.get(clave);
const resultado = fn.apply(this, args);
caché.set(clave, resultado);
return resultado;
};
}
const fnCostosa = memoizar((n) => {
console.log('Calculando...');
return n * n;
});
fnCostosa(5); // Imprime "Calculando...", retorna 25
fnCostosa(5); // Retorna 25 de inmediato (desde caché)
// Aplicación parcial
function multiplicar(a, b) { return a * b; }
function parcial(fn, ...argsPreset) {
return function (...argsRestantes) {
return fn(...argsPreset, ...argsRestantes);
};
}
const doble = parcial(multiplicar, 2);
const triple = parcial(multiplicar, 3);
doble(5); // 10
triple(5); // 15
El Error Clásico del Bucle
// ERROR: var es de scope de función — todos los callbacks comparten el mismo `i`
for (var i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // Imprime: 3, 3, 3
}
// SOLUCIÓN 1: Usar let (scope de bloque)
for (let i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 100); // Imprime: 0, 1, 2
}
// SOLUCIÓN 2: Usar IIFE para crear nuevo scope por iteración
for (var i = 0; i < 3; i++) {
((j) => setTimeout(() => console.log(j), 100))(i); // Imprime: 0, 1, 2
}
Hoisting
// Las declaraciones de función son totalmente "hoisted"
saludar(); // ✅ Funciona!
function saludar() { console.log('Hola'); }
// var es "declaration-hoisted" (pero no inicializado)
console.log(x); // undefined (sin error)
var x = 5;
// let/const están en la "temporal dead zone" hasta su declaración
// console.log(y); // ❌ ReferenceError
let y = 10;