En informática, un cierre es una función que tiene un entorno propio. En este entorno, hay al menos una variable vinculada (un nombre que tiene un valor, como un número). El entorno de la clausura mantiene las variables ligadas en memoria entre los usos de la clausura.

Peter J. Landin dio a esta idea el nombre de cierre en 1964. El lenguaje de programación Scheme popularizó los cierres después de 1975. Muchos lenguajes de programación hechos después de esa época tienen cierres.

Las funciones anónimas (funciones sin nombre) a veces se denominan erróneamente cierres. La mayoría de los lenguajes que tienen funciones anónimas también tienen cierres. Una función anónima también es un cierre si tiene un entorno propio con al menos una variable vinculada. Una función anónima sin entorno propio no es un cierre. Un cierre con nombre no es anónimo.

Cómo funcionan los cierres

Un cierre combina tres elementos: la propia función, las variables del alcance (scope) en el momento de su definición y una referencia al entorno donde esas variables residen. Cuando la función se ejecuta más tarde, sigue teniendo acceso a esas variables aunque la función que las creó haya finalizado. Esto se debe a que el entorno (o contexto léxico) se conserva en memoria mientras exista la clausura.

Hay dos conceptos importantes relacionados con los cierres:

  • Alcance léxico (lexical scope): las variables que la clausura captura son las que estaban visibles en el lugar donde la función fue definida.
  • Captura por referencia vs por valor: algunos lenguajes (por ejemplo, JavaScript) capturan referencias a las variables, de modo que si la variable cambia después, la clausura ve el nuevo valor. Otros lenguajes o mecanismos pueden copiar el valor en el momento de la creación.

Ejemplos prácticos

Ejemplo sencillo en JavaScript — una función que genera contadores independientes:

function crearContador() {   let cuenta = 0;   return function() {     cuenta += 1;     return cuenta;   }; }  const c1 = crearContador(); console.log(c1()); // 1 console.log(c1()); // 2  const c2 = crearContador(); console.log(c2()); // 1

En este ejemplo, cada llamada a crearContador devuelve una clausura que mantiene su propia variable cuenta en memoria.

Ejemplo en Python:

def crear_multiplicador(n):     def multiplicar(x):         return x * n     return multiplicar  doble = crear_multiplicador(2) print(doble(5))  # 10

Aquí, la clausura multiplicar «recuerda» el valor de n incluso después de que crear_multiplicador haya terminado.

Usos comunes de los cierres

  • Encapsulamiento y estado privado: mantener variables privadas accesibles solo por funciones internas.
  • Funciones fábrica: generar funciones configuradas con parámetros predefinidos.
  • Callbacks y controladores de eventos: acceder a datos del contexto desde manejadores asíncronos.
  • Programación funcional: currying y aplicación parcial de funciones.

Errores comunes y cómo evitarlos

  • Captura de variables en bucles: en JavaScript, usar var en un bucle puede llevar a que todas las clausuras compartan la misma variable. Solución: usar let (alcance de bloque) o crear una función intermedia para fijar el valor.
  • Retener memoria innecesariamente: como las clausuras mantienen referencias al entorno, pueden impedir que el recolector de basura libere datos si no se gestionan bien. Liberar referencias o evitar variables grandes en el entorno puede ayudar.
  • Confusión entre función anónima y cierre: recordar que no toda función anónima es un cierre; solo lo es si captura al menos una variable del entorno.

Consideraciones de implementación y rendimiento

Los cierres suelen implementarse almacenando las variables capturadas en una estructura que sobrevive más allá del ciclo de vida de la función original (por ejemplo, en el heap en vez de la pila). Esto facilita la reutilización pero puede aumentar el coste de memoria y afectar el rendimiento si se crean muchas clausuras con grandes entornos.

Lenguajes con recolección de basura manejan la mayoría de los casos de forma segura; en lenguajes sin GC, el programador o el compilador debe gestionar la vida de esos entornos explícitamente.

Lenguajes que usan cierres

Muchos lenguajes modernos soportan cierres, entre ellos JavaScript, Python, Ruby, Scheme, Haskell, Lua, Rust (mediante closures con diferentes modalidades de captura), y muchos más. Scheme y otros lenguajes de la familia Lisp fueron fundamentales para popularizar y formalizar el concepto.

Términos relacionados

  • Clausura / cierre: ambas traducciones son usadas en español; «cierre» es la más habitual en textos técnicos, mientras que «clausura» también aparece por influencia del término inglés closure.
  • Alcance léxico vs dinámico: los cierres se basan en el alcance léxico; en sistemas de alcance dinámico las variables capturadas dependen del punto de ejecución, no del punto de definición.

En resumen, un cierre es una herramienta poderosa para conservar estado y contexto en funciones, útil en multitud de patrones de diseño y estilos de programación. Entender cómo y cuándo se capturan las variables, así como las implicaciones de memoria, ayuda a usar cierres de forma efectiva y segura.