W3docs

Closures

Aprende los closures en JavaScript: cómo una función recuerda su ámbito léxico, con ejemplos de privacidad de datos, fábricas de funciones y bucles.

Entendiendo los Closures en JavaScript

En JavaScript, los closures son un concepto fundamental y poderoso que permite crear funciones con datos preservados de su entorno léxico. Esta guía ofrece una exploración profunda de los closures, con explicaciones detalladas y ejemplos de código prácticos para afianzar tu comprensión.

¿Qué es un Closure?

Un closure es la combinación de una función y el entorno léxico dentro del cual fue declarada esa función. Este entorno consiste en todas las variables locales que estaban en el ámbito en el momento en que se creó el closure.

Ejemplo de un Closure

Considera el siguiente ejemplo:

javascript— editable

En este ejemplo, greetByName es un closure. Captura las variables greeting y name del ámbito de la función greet.

Cuando se llama a la función greet con el argumento 'John', crea una variable local greeting y una función greetByName. La función greet luego devuelve la función greetByName. Incluso después de que la función greet ha terminado de ejecutarse, la función greetByName devuelta sigue teniendo acceso a las variables greeting y name. Esto se debe a que greetByName es un closure, lo que significa que recuerda el entorno donde fue creada.

¿Por Qué Usar Closures?

Los closures permiten la encapsulación de datos y la creación de variables privadas. Permiten que las funciones conserven el acceso a las variables de su ámbito contenedor (envolvente) incluso después de que ese ámbito haya terminado de ejecutarse.

Creando un Closure Básico

Considera el siguiente ejemplo:

javascript— editable

Cuando se invoca outerFunction, crea outerVariable e innerFunction. Luego devuelve innerFunction. La innerFunction devuelta conserva el acceso a outerVariable incluso después de que outerFunction ha terminado de ejecutarse.

Casos de Uso Prácticos de los Closures

Privacidad de Datos

Los closures pueden usarse para crear datos privados que no pueden ser accedidos desde el ámbito global.

javascript— editable

En este ejemplo, count es una variable privada a la que solo puede acceder y modificar la función anónima devuelta. Cada vez que se llama a la función, incrementa y devuelve el count actualizado.

Un punto sutil pero importante: cada llamada a createCounter produce su propio closure independiente con su propio count privado. Dos contadores creados por separado no comparten estado:

javascript— editable

Cada invocación de la función exterior crea un entorno léxico completamente nuevo, por lo que a y b incrementan variables separadas.

Fábricas de Funciones

Los closures pueden usarse para crear fábricas de funciones que generan funciones especializadas.

javascript— editable

Aquí, createMultiplier genera funciones que multiplican un número dado por un multiplier especificado. Cada función devuelta conserva el acceso al valor de multiplier a través del closure.

Closures en Bucles

Los closures pueden ser complicados cuando se usan dentro de bucles. Considera el siguiente ejemplo:

javascript— editable

En este código, el bucle termina y luego los tres callbacks de setTimeout se ejecutan, registrando el valor de i, que es 4 después de que el bucle finaliza. Esto ocurre porque var tiene ámbito de función.

Para corregir esto, usa let, que tiene ámbito de bloque:

javascript— editable
Información

Usa siempre let o const en lugar de var para evitar problemas de ámbito y garantizar que las variables tengan el alcance adecuado.

Por Qué Funciona la Versión con let

La clave es que let crea un nuevo enlace de i en cada iteración del bucle. Cada callback de setTimeout cierra sobre un i diferente, por lo que recuerda el valor que existía durante su propia iteración. Con var, solo hay un i compartido por todos los callbacks, y cuando los temporizadores se disparan, el bucle ya lo ha llevado a 4.

Antes de que existiera let, la solución clásica era crear un ámbito nuevo por iteración con una IIFE:

javascript— editable

La función invocada inmediatamente copia el valor actual de i en su propio parámetro captured, dando a cada callback una copia privada sobre la que cerrar.

Closures y Gestión de Memoria

Los closures pueden causar fugas de memoria si no se usan correctamente. Dado que los closures conservan referencias a su ámbito exterior, las variables no utilizadas pueden permanecer en memoria más tiempo del necesario.

Advertencia

Ten en cuenta el uso de memoria al crear closures. Asegúrate de que los closures no capturen innecesariamente objetos grandes o elementos del DOM para prevenir fugas de memoria.

Patrones Avanzados de Closures

Patrón de Módulo

El patrón de módulo aprovecha los closures para crear miembros privados y públicos dentro de una función.

javascript— editable

En este ejemplo, CounterModule es una expresión de función invocada inmediatamente (IIFE) que devuelve un objeto con métodos públicos (increment y reset). La variable count permanece privada y no puede ser accedida directamente.

Closures en Frameworks Modernos de JavaScript

Los closures son especialmente importantes en los frameworks modernos de JavaScript como React. En React, los closures ayudan a gestionar el estado y los efectos secundarios dentro de los componentes funcionales.

import React, { useState } from 'react';

function Counter() {
    const [count, setCount] = useState(0);

    function increment() {
        setCount(prevCount => prevCount + 1);
    }

    return (
        <div>
            <p>{count}</p>
            <button onClick={increment}>Increment</button>
        </div>
    );
}

En este ejemplo, useState se usa para crear una variable de estado count y una función setCount para actualizarla. La función increment forma un closure, capturando setCount y count del ámbito del componente. Nota: en React, los closures pueden a veces capturar estado obsoleto si las dependencias no se gestionan correctamente en hooks como useEffect.

Información

Practica crear y usar closures en diferentes contextos para comprender plenamente su potencial y sus limitaciones. Son una herramienta esencial en el arsenal de cualquier desarrollador de JavaScript.

Buenas Prácticas para Usar Closures

  1. Limitar el Ámbito: Evita crear closures dentro de bucles o estructuras profundamente anidadas a menos que sea necesario, para prevenir fugas de memoria y problemas de rendimiento.
  2. Minimizar las Variables Capturadas: Captura solo las variables que necesitas dentro del closure para reducir el consumo de memoria.
  3. Evitar Capturas Grandes: No captures objetos grandes o elementos del DOM dentro de closures para prevenir la retención innecesaria de memoria.
  4. Usar let y const: Prefiere siempre let o const sobre var para garantizar un ámbito adecuado y evitar comportamientos no deseados.
  5. Limpiar: Siempre que sea posible, elimina las referencias capturadas por los closures para evitar fugas de memoria, especialmente en aplicaciones de larga duración.

Conclusión

Los closures son una característica poderosa de JavaScript que permite la privacidad de datos, las fábricas de funciones y patrones avanzados como los módulos. Entender y utilizar los closures de manera efectiva puede llevar a un código más robusto y mantenible. Al dominar los closures, mejorarás tu capacidad para escribir código JavaScript eficiente, seguro y limpio.

Práctica

Práctica
¿Qué son los closures en JavaScript?
¿Qué son los closures en JavaScript?
Práctica
¿Por qué un bucle 'for' usando 'var' con setTimeout registra el mismo valor final tres veces, mientras que 'let' registra 1, 2, 3?
¿Por qué un bucle 'for' usando 'var' con setTimeout registra el mismo valor final tres veces, mientras que 'let' registra 1, 2, 3?
Was this page helpful?