Alcance y Closures en JavaScript

Alcance y Closures en JavaScript

Alcance (Scope)

La palabra reservada var permite declraar variables múltiples veces. Al declarar una variable existente, JavaScript omite la declaración y continúa con la asignación.

En modo no estricto, si se asigna un valor a una variable sin declararla (a = 2), JavaScript crea automáticamente una variable global llamada a. En modo estricto, esto genera un error y se prohíbe la creación implícita de variables globales.

function procesar(valor) {
    console.log(valor + resultado);
    resultado = valor;
}
procesar(2); // ReferenceError: resultado is not defined, resultado no está en el alcance actual

function procesar(valor) {
    console.log(valor + resultado);
    let resultado = valor;
}
procesar(2); // ReferenceError: Cannot access 'resultado' before initialization, let no permite elevación de variables

function procesar(valor) {
    console.log(valor + resultado);
    var resultado = valor;
}
procesar(2); // NaN, var permite elevación, resultado está declarado pero sin valor, devuelve undefined

function procesar(valor) {
    var resultado = valor;
}
procesar(2);
console.log(resultado); // ReferenceError: resultado is not defined, la variable var solo es accesible dentro de la función

function procesar(valor) {
    resultado = valor;
}
procesar(2);
console.log(resultado); // 2, resultado es una variable global

function procesar(valor) {
    resultado = valor;
}
var resultado = 3;
procesar(2);
console.log(resultado); // 2, la función modificó la variable global

Las variables declaradas con let y const no se añaden al objeto window, a diferencia de las declaradas con var:

valorGlobal = 1;
var varGlobal = 2;
let letGlobal = 3;
console.log(window.valorGlobal, window.varGlobal, window.letGlobal); // 1,2,undefined

El alcance de bloque permite que las variables sean destruidas cuando ya no son necesarias:

function procesar(datos) {
    // Realizar operaciones con los datos
}

// someReallyBigData se destruirá automáticamente después de este bloque
{
    let someReallyBigData = { ... };
    procesar(someReallyBigData);
}

La elevación (hoisting) afecta tanto a variables como a funciones, pero con diferentes comportamientos:

ejecutarFuncion();
function ejecutarFuncion() {
    console.log(variable); // undefined
    var variable = 2;
}
console.log(variable); // ReferenceError: variable is not defined

ejecutarFunc(); // TypeError, la función existe pero es undefined
var ejecutarFunc = function() {
    // ...
};

Closures

Un closure ocurre cuando una función mantiene una referencia a su ámbito léxico original, incluso cuando esa función se ejecuta fuera de ese ámbito.

function crearCalculadora() {
    let contador = 0;
    function incrementar() {
        contador++;
        console.log(contador);
    }
    return incrementar;
}

const miCalculadora = crearCalculadora();
miCalculadora(); // 1
miCalculadora(); // 2

Los closures son útiles para manejar operaciones asíncronas como setTimeout:

// Problema: todos los timers muestran el mismo valor final
for (var i = 1; i <= 5; i++) {
    setTimeout(function() {
        console.log(i);
    }, i * 1000);
}

// Solución 1: Usar una función autoejecutable para capturar el valor actual
for (var i = 1; i <= 5; i++) {
    (function(valorActual) {
        setTimeout(function() {
            console.log(valorActual);
        }, valorActual * 1000);
    })(i);
}

// Solución 2: Usar let que crea un nuevo binding por cada iteración
for (let i = 1; i <= 5; i++) {
    setTimeout(function() {
        console.log(i);
    }, i * 1000);
}

Los closures también pueden utilizarse para crear encapsulación y proteger datos:

function crearContadorPrivado() {
    let contador = 0;
    
    return {
        incrementar: function() {
            return ++contador;
        },
        decrementar: function() {
            return --contador;
        },
        obtenerValor: function() {
            return contador;
        }
    };
}

const miContador = crearContadorPrivado();
console.log(miContador.incrementar()); // 1
console.log(miContador.incrementar()); // 2
console.log(miContador.obtenerValor()); // 2
// No hay forma directa de acceder o modificar contador desde fuera

Etiquetas: JavaScript Alcance closures Hoisting variables

Publicado el 7-24 18:40