Closures
========

Un **closure** es la combinación de una función agrupada (dentro de otra) con referencias a su estado adyacente (el **entorno léxico**). En otras palabras, un *closure* te da acceso al alcance de una función externa desde una función interna. En JavaScript, los *closure* se crean cada vez que se crea una función, en el momento de la creación de la función.

Ámbito léxico
-------------

Considere el siguiente ejemplo:


```js
function init() {
  var name = "Mozilla"; // name es una variable local creada por init
  function displayName() {
    // displayName() es la función interna que forma el closure
    console.log(name); // usar la variable declarada en la función padre
  }
  displayName();
}
init();
```


`init()` crea una variable local llamada `name` y una función llamada `displayName()`. La función `displayName()` es una función interna que se define dentro de `init()` y está disponible solo dentro del cuerpo de la función `init()`. Tenga en cuenta que la función `displayName()` no tiene variables locales propias. Sin embargo, dado que las funciones internas tienen acceso a las variables de las funciones externas, `displayName()` puede acceder a la variable `name` declarada en la función principal, `init()`.

Ejecute el código utilizando este enlace de JSFiddle y observe que la instrucción `console.log()` dentro de la función `displayName()` muestra con éxito el valor de la variable `name`, que se declara en su función principal. Este es un ejemplo de *ámbito léxico*, que describe cómo un analizador resuelve los nombres de variables cuando las funciones están anidadas. La palabra *léxico* se refiere al hecho de que el ámbito léxico utiliza la ubicación donde se declara una variable dentro del código fuente para determinar dónde está disponible esa variable. Las funciones anidadas tienen acceso a variables declaradas en su ámbito externo.

En este ejemplo particular, el ámbito se denomina *ámbito de función* o *alcance de la función*, porque la variable es accesible y solo es accesible dentro del cuerpo de la función donde se declara.

### Alcance con let y const

Tradicionalmente (antes de ES6), JavaScript solo tenía dos tipos de ámbitos: *ámbito de la función* y *ámbito global*. Las variables declaradas con `var` tienen un alcance de función o un alcance global, dependiendo de si se declaran dentro de una función o fuera de una función. Esto puede ser complicado, porque los bloques con llaves rizadas no crean ámbitos:


```js
if (Math.random() > 0.5) {
  var x = 1;
} else {
  var x = 2;
}
console.log(x);
```


Para personas de otros lenguajes (por ejemplo, C, Java) donde los bloques crean ámbitos, el código anterior debería arrojar un error en la línea `console.log`, porque estamos fuera del alcance de `x` en cualquiera de los bloques. Sin embargo, debido a que los bloques no crean ámbitos para `var`, las instrucciones `var` aquí en realidad crean una variable global. También se presenta a continuación un ejemplo práctico que ilustra cómo esto puede causar errores reales cuando se combina con *closures*.

En ES6, JavaScript introdujo las declaraciones `let` y `const`, que, entre otras cosas, como zonas muertas temporales, le permiten crear variables de alcance de bloque.


```js
if (Math.random() > 0.5) {
  const x = 1;
} else {
  const x = 2;
}
console.log(x); //ReferenceError: x no está definido
```


En esencia, los bloques finalmente se tratan como ámbitos en ES6, pero solo si declaras variables con `let` o `const`. Además, ES6 introdujo módulos, que introdujo otro tipo de alcance. Los *closure* son capaces de capturar variables en todos estos ámbitos, que introduciremos más adelante.

Closure
-------

Considere el siguiente ejemplo:


```js
function makeFunc() {
  const name = "Mozilla";
  function displayName() {
    console.log(name);
  }
  return displayName;
}

const myFunc = makeFunc();
myFunc();
```


Ejecutar este código tiene exactamente el mismo efecto que el ejemplo anterior de la función `init()` anterior. Lo que es diferente (e interesante) es que la función interna `displayName()` se devuelve desde la función externa *antes de ejecutarse*.

A primera vista, puede parecer poco intuitivo que este código siga funcionando. En algunos lenguajes de programación, las variables locales dentro de una función existen solo durante la ejecución de esa función. Una vez que `makeFunc()` termine de ejecutarse, es de esperar que la variable `name` ya no sea accesible. Sin embargo, debido a que el código sigue funcionando como se esperaba, este obviamente no es el caso en JavaScript.

La razón es que las funciones en JavaScript forman *closures*. Un *closure* es la combinación de una función y el entorno léxico dentro del cual se declaró esa función. Este entorno consiste en cualquier variable local que estuviera dentro del alcance en el momento en que se creó el *closure*. En este caso, `myFunc` es una referencia a la instancia de la función `displayName` que se crea cuando se ejecuta `makeFunc`. La instancia de `displayName` mantiene una referencia a su entorno léxico, dentro del cual existe la variable `name`. Por esta razón, cuando se invoca `myFunc`, la variable `name` permanece disponible para su uso, y 'Mozilla' se pasa a `console.log`.

Aquí hay un ejemplo un poco más interesante: una función `makeAdder`:


```js
function makeAdder(x) {
  return function (y) {
    return x + y;
  };
}

const add5 = makeAdder(5);
const add10 = makeAdder(10);

console.log(add5(2)); // 7
console.log(add10(2)); // 12
```


En este ejemplo, hemos definido una función `makeAdder(x)`, que toma un solo argumento `x` y devuelve una nueva función. La función que devuelve toma un solo argumento `y` y devuelve la suma de la variable `x` y la variable `y`.

En esencia, `makeAdder` es una fábrica de funciones. Crea funciones que pueden añadir un valor específico a su argumento. En el ejemplo anterior, la fábrica de funciones crea dos nuevas funciones: una que suma cinco a su argumento y otra que suma 10.

`add5` y `add10` forman *closures*. Comparten la misma definición de cuerpo de función, pero almacenan diferentes entornos léxicos. En el entorno léxico de `add5`, `x` es 5, mientras que en el entorno léxico de `add10`, `x` es 10.

Closure prácticos
-----------------

Los *closure* son útiles porque te permiten asociar datos (el entorno léxico) con una función que opera sobre esos datos. Esto tiene paralelismos obvios con la programación orientada a objetos, donde los objetos le permiten asociar datos (las propiedades del objeto) con uno o más métodos.

En consecuencia, puede usar un *closure* en cualquier lugar donde normalmente pueda usar un objeto con un solo método.

Las situaciones en las que es posible que desee hacer esto son particularmente comunes en la web. Gran parte del código escrito en JavaScript para *front-end* está basado en eventos. Defina algún comportamiento y luego adjúntelo a un evento activado por el usuario (como un clic o una pulsación de tecla). El código se adjunta como una devolución de llamada (una sola función que se ejecuta en respuesta al evento).

Por ejemplo, supongamos que queremos añadir botones a una página para ajustar el tamaño del texto. Una forma de hacerlo es especificar el tamaño de fuente del elemento `body` (en píxeles) y luego establecer el tamaño de los otros elementos de la página (como los encabezados) utilizando la unidad `em` relativa:


```css
body {
  font-family: Helvetica, Arial, sans-serif;
  font-size: 12px;
}

h1 {
  font-size: 1.5em;
}

h2 {
  font-size: 1.2em;
}
```


Dichos botones interactivos de tamaño de texto pueden cambiar la propiedad `font-size` del elemento `body`, y los ajustes son recogidos por otros elementos de la página gracias a las unidades relativas.

Aquí está el código JavaScript:


```js
function makeSizer(size) {
  return function () {
    document.body.style.fontSize = `${size}px`;
  };
}

const size12 = makeSizer(12);
const size14 = makeSizer(14);
const size16 = makeSizer(16);
```


`size12`, `size14` y `size16` son ahora funciones que cambian el tamaño del texto del cuerpo a 12, 14 y 16 píxeles, respectivamente. Puede adjuntarlos a los botones como se muestra en el siguiente ejemplo de código.


```js
document.getElementById("size-12").onclick = size12;
document.getElementById("size-14").onclick = size14;
document.getElementById("size-16").onclick = size16;
```



```html
<button id="size-12">12</button>
<button id="size-14">14</button>
<button id="size-16">16</button>
```


Ejecuta el código usando JSFiddle.

Emular métodos privados con *closures*
--------------------------------------

Los lenguajes como Java le permiten declarar métodos como privados, lo que significa que solo pueden ser llamados por otros métodos de la misma clase.

JavaScript, antes de clases, no tenía una forma nativa de declarar métodos privados, pero era posible emular métodos privados usando *closures*. Los métodos privados no solo son útiles para restringir el acceso al código. También proporcionan una forma poderosa de gestionar su espacio de nombres global.

El siguiente código ilustra cómo usar *closures* para definir funciones públicas que pueden acceder a funciones y variables privadas. Tenga en cuenta que estos *closures* siguen el Patrón de diseño de módulo.


```js
const counter = (function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }

  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
})();

console.log(counter.value()); // 0.

counter.increment();
counter.increment();
console.log(counter.value()); // 2.

counter.decrement();
console.log(counter.value()); // 1.
```


En ejemplos anteriores, cada *closure* tenía su propio entorno léxico. Aquí, sin embargo, hay un único entorno léxico que es compartido por las tres funciones: `counter.increment`, `counter.decrement` y `counter.value`.

El entorno léxico compartido se crea en el cuerpo de una función anónima, *que se ejecuta tan pronto como se ha definido* (también conocida como IIFE). El entorno léxico contiene dos elementos privados: una variable llamada `privateCounter` y una función llamada `changeBy`. No puedes acceder a ninguno de estos miembros privados desde fuera de la función anónima. En su lugar, puede acceder a ellos utilizando las tres funciones públicas que se devuelven desde el contenedor anónimo.

Esas tres funciones públicas forman *closures* que comparten un mismo entorno léxico. Gracias al alcance léxico de JavaScript, cada uno tiene acceso a la variable `privateCounter` y a la función `changeBy`.


```js
const makeCounter = function () {
  let privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment() {
      changeBy(1);
    },

    decrement() {
      changeBy(-1);
    },

    value() {
      return privateCounter;
    },
  };
};

const counter1 = makeCounter();
const counter2 = makeCounter();

console.log(counter1.value()); // 0.

counter1.increment();
counter1.increment();
console.log(counter1.value()); // 2.

counter1.decrement();
console.log(counter1.value()); // 1.
console.log(counter2.value()); // 0.
```


Observa cómo los dos contadores mantienen su independencia el uno del otro. Cada *closure* hace referencia a una versión diferente de la variable `privateCounter` a través de su propio *closure*. Cada vez que se llama a uno de los contadores, su entorno léxico cambia cambiando el valor de esta variable. Los cambios en el valor de la variable en un *closure* no afectan el valor en el otro *closure*.

**Nota:** El uso de *closures* de esta manera proporciona beneficios que normalmente se asocian con la programación orientada a objetos. En particular, *ocultación de datos* y *encapsulación*.

Cadena de alcance de closure
----------------------------

Cada *closure* tiene tres alcances:

* Alcance local (Ámbito propio)
* Alcance envolvente (puede ser alcance de bloque, función o módulo)
* Alcance global

Un error común es no darse cuenta de que en el caso de que la función externa sea en sí misma una función anidada, el acceso al alcance de la función externa incluye el alcance circundante de la función externa, creando efectivamente una cadena de alcances de función. Para demostrarlo, considere el siguiente código de ejemplo.


```js
// ámbito global
const e = 10;
function sum(a) {
  return function (b) {
    return function (c) {
      // ámbito de funciones exteriores
      return function (d) {
        // ámbito local
        return a + b + c + d + e;
      };
    };
  };
}

console.log(sum(1)(2)(3)(4)); // 20
```


También puede escribir sin funciones anónimas:


```js
// ámbito global
const e = 10;
function sum(a) {
  return function sum2(b) {
    return function sum3(c) {
      // ámbito de funciones exteriores
      return function sum4(d) {
        // ámbito local
        return a + b + c + d + e;
      };
    };
  };
}

const sum2 = sum(1);
const sum3 = sum2(2);
const sum4 = sum3(3);
const result = sum4(4);
console.log(result); // 20
```


En el ejemplo anterior, hay una serie de funciones anidadas, todas las cuales tienen acceso al ámbito de las funciones externas. En este contexto, podemos decir que los *closures* tienen acceso a *todos* los ámbitos de función externos.

Los *closure* también pueden capturar variables en ámbitos de bloque y ámbitos de módulo. Por ejemplo, lo siguiente crea un *closure* sobre la variable de ámbito de bloque `y`:


```js
function outer() {
  let getY;
  {
    const y = 6;
    getY = () => y;
  }
  console.log(typeof y); // undefined
  console.log(getY()); // 6
}

outer();
```


Los *closure* sobre los módulos pueden ser más interesantes.


```js
// myModule.js
let x = 5;
export const getX = () => x;
export const setX = (val) => {
  x = val;
};
```


Aquí, el módulo exporta un par de funciones *getter-setter* (para asignar y obtener), que se cierran sobre la variable de alcance del módulo `x`. Incluso cuando `x` no es directamente accesible desde otros módulos, se puede leer y escribir con las funciones.


```js
import { getX, setX } from "./myModule.js";

console.log(getX()); // 5
setX(6);
console.log(getX()); // 6
```


Los *closure* también pueden cerrar sobre valores importados, que se consideran como *enlazadas (en-US)* en vivo, porque cuando el valor original cambia, el importado cambia en consecuencia.


```js
// myModule.js
export let x = 1;
export const setX = (val) => {
  x = val;
};
```



```js
// closureCreator.js
import { x } from "./myModule.js";

export const getX = () => x; // Cerrar sobre un enlace en vivo importado
```



```js
import { getX } from "./closureCreator.js";
import { setX } from "./myModule.js";

console.log(getX()); // 1
setX(2);
console.log(getX()); // 2
```

Crear closures en bucles: un error común
----------------------------------------

Antes de la introducción de la palabra clave `let`, se producía un problema común con los *closure* cuando los creabas dentro de un bucle. Para demostrarlo, considere el siguiente código de ejemplo.


```html
<p id="help">Aquí aparecerán notas útiles</p>
<p>Correo electrónico: <input type="text" id="email" name="email" /></p>
<p>Nombre: <input type="text" id="name" name="name" /></p>
<p>Edad: <input type="text" id="age" name="age" /></p>
```



```js
function showHelp(help) {
  document.getElementById("help").textContent = help;
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Tu dirección de correo electrónico" },
    { id: "name", help: "Tu nombre completo" },
    { id: "age", help: "Tu edad (debes ser mayor de 16 años)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    // La razón es el uso de `var` en esta línea
    var item = helpText[i];
    document.getElementById(item.id).onfocus = function () {
      showHelp(item.help);
    };
  }
}

setupHelp();
```


Intenta ejecutar el código en JSFiddle.

La matriz `helpText` define tres consejos útiles, cada uno asociado con el ID de un campo de entrada en el documento. El bucle recorre estas definiciones, conectando un evento `onfocus` a cada uno que muestra el método de ayuda asociado.

Si pruebas este código, verás que no funciona como esperabas. No importa en qué campo se centre, se mostrará el mensaje sobre su edad.

La razón de esto es que las funciones asignadas a `onfocus` forman *closures*; consisten en la definición de la función y el entorno capturado desde el alcance de la función `setupHelp`. El bucle ha creado tres *closure*, pero cada uno comparte el mismo entorno léxico único, que tiene una variable con valores cambiantes (`item`). Esto se debe a que la variable `item` se declara con `var` y, por lo tanto, tiene un alcance de función debido a la elevación. El valor de `item.help' se determina cuando se ejecutan las devoluciones de llamada` onfocus`. Debido a que el bucle ya ha seguido su curso en ese momento, el objeto variable` item`(compartido por los tres _closure_) se ha dejado apuntando a la última entrada en la lista`helpText`.

Una solución en este caso es usar más *closure*: en particular, usar una fábrica de funciones como se describió anteriormente:


```js
function showHelp(help) {
  document.getElementById("help").textContent = help;
}

function makeHelpCallback(help) {
  return function () {
    showHelp(help);
  };
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Tu dirección de correo electrónico" },
    { id: "name", help: "Tu nombre completo" },
    { id: "age", help: "Tu edad (debes ser mayor de 16 años)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    var item = helpText[i];
    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);
  }
}

setupHelp();
```


Ejecuta el código usando este enlace de JSFiddle.

Esto funciona como se esperaba. En lugar de que todas las devoluciones de llamada compartan un único entorno léxico, la función `makeHelpCallback` crea *un nuevo entorno léxico* para cada devolución de llamada, en el que `help` se refiere a la cadena correspondiente de la matriz `helpText`.

Otra forma de escribir lo anterior utilizando cierres anónimos es:


```js
function showHelp(help) {
  document.getElementById("help").textContent = help;
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Tu dirección de correo electrónico" },
    { id: "name", help: "Tu nombre completo" },
    { id: "age", help: "Tu edad (debes ser mayor de 16 años)" },
  ];

  for (var i = 0; i < helpText.length; i++) {
    (function () {
      var item = helpText[i];
      document.getElementById(item.id).onfocus = function () {
        showHelp(item.help);
      };
    })(); //Adjunto del detector de eventos inmediatos con el valor actual del elemento (conservado hasta la iteración).
  }
}

setupHelp();
```


Si no desea utilizar más *closure*, puede utilizar la palabra clave `let` o `const`:


```js
function showHelp(help) {
  document.getElementById("help").textContent = help;
}

function setupHelp() {
  const helpText = [
    { id: "email", help: "Tu dirección de correo electrónico" },
    { id: "name", help: "Tu nombre completo" },
    { id: "age", help: "Tu edad (debes ser mayor de 16 años)" },
  ];

  for (let i = 0; i < helpText.length; i++) {
    const item = helpText[i];
    document.getElementById(item.id).onfocus = () => {
      showHelp(item.help);
    };
  }
}

setupHelp();
```


Este ejemplo usa `const` en lugar de `var`, por lo que cada *closure* vincula la variable de alcance de bloque, lo que significa que no se requieren *closure* adicionales.

Otra alternativa podría ser usar `forEach()` para iterar sobre la matriz `helpText` y adjuntar un detector a cada `<input>`, como se muestra:


```js
function showHelp(help) {
  document.getElementById("help").textContent = help;
}

function setupHelp() {
  var helpText = [
    { id: "email", help: "Tu dirección de correo electrónico" },
    { id: "name", help: "Tu nombre completo" },
    { id: "age", help: "Tu edad (debes ser mayor de 16 años)" },
  ];

  helpText.forEach(function (text) {
    document.getElementById(text.id).onfocus = function () {
      showHelp(text.help);
    };
  });
}

setupHelp();
```

Consideraciones de rendimiento
------------------------------

Como se mencionó anteriormente, cada instancia de función gestiona su propio alcance y cierre. Por lo tanto, no es prudente crear funciones innecesariamente dentro de otras funciones si no se necesitan *closures* para una tarea en particular, ya que afectará negativamente el rendimiento del script tanto en términos de velocidad de procesamiento como de consumo de memoria.

Por ejemplo, al crear un nuevo objeto/clase, los métodos normalmente deben asociarse al prototipo del objeto en lugar de definirse en el constructor del objeto. La razón es que cada vez que se llama al constructor, los métodos se reasignan (es decir, para cada creación de objetos).

Observemos el siguiente caso:


```js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
  this.getName = function () {
    return this.name;
  };

  this.getMessage = function () {
    return this.message;
  };
}
```


Debido a que el código anterior no aprovecha los beneficios de usar *closures* en esta instancia en particular, podríamos reescribirlo para evitar usar *closures* de la siguiente manera:


```js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype = {
  getName() {
    return this.name;
  },
  getMessage() {
    return this.message;
  },
};
```


Sin embargo, no se recomienda redefinir el prototipo. En su lugar, el siguiente ejemplo se adjunta al prototipo existente:


```js
function MyObject(name, message) {
  this.name = name.toString();
  this.message = message.toString();
}
MyObject.prototype.getName = function () {
  return this.name;
};
MyObject.prototype.getMessage = function () {
  return this.message;
};
```


En los dos ejemplos anteriores, el prototipo heredado puede ser compartido por todos los objetos y las definiciones del método no necesitan ocurrir en cada creación de objetos. Consulte La herencia y la cadena de prototipos para obtener más información.