Describir el concepto de cierre en JavaScript

En este artículo, aprenderá sobre el concepto de cierre en Javascript y cómo aplicarlo en varias funciones. El cierre en javascript es una forma de combinar todas las funciones y vincularlas en paquetes con el entorno léxico. Profundizaremos en el concepto de cierre a medida que crezcamos en este artículo. 

Requisito previo: antes de continuar con nuestra discusión principal, debe tener un buen conocimiento práctico de las funciones de javascript, es decir, cómo se lleva a cabo la asignación de memoria, el concepto de alcance variable, el entorno léxico en javascript. 

Comencemos primero discutiendo los ejemplos básicos del programa javascript:

En caso de que no sepa cómo agregar y ejecutar javascript en el documento HTML, consulte ¿Dónde colocar JavaScript en un documento HTML? artículo para un conocimiento más detallado.

Veremos rápidamente cómo se ejecuta el código javascript. Para esto, agregue la etiqueta <script> en el archivo index.html para una referencia al código javascript externo o defina el código dentro de la etiqueta <script>. Posteriormente, abra la pestaña de la consola del desarrollador en el navegador para ver el resultado del código.  

index.html

<!DOCTYPE html>
<html>
  
<head>
    <title>Closures</title>
</head>
  
<body>
    <h2>Closures in Javascript</h2>
    <script src="./script.js"></script>
</body>
  
</html>

Ahora, comprenderemos todos los conceptos básicos, como cómo funciona la función, cómo se puede declarar una función dentro de otra función usando la string de alcance, devolver la función desde la función, a través de los ejemplos, luego comprenderemos el concepto de cierre.

Ejemplo 1: aquí, simplemente ilustramos el funcionamiento de la función como se muestra a continuación:

script.js

function a() {
  var x = 5;
  console.log(x);
}
  
function b() {
  var y = 15;
  console.log(y);
}
a();
b();

Explicación: 

  1. En primer lugar, se creará un contexto de ejecución global y luego las definiciones de funciones obtendrán espacio en la memoria.
  2. Tan pronto como encuentra javascript, una primera función llama a(), crea otro contexto de ejecución y reserva memoria para la variable x y coloca undefined dentro de x.
  3. Luego, el hilo de la fase de ejecución entra en escena y la variable x obtiene el valor 5 y en la siguiente línea, console.log imprime el valor de x, y finalmente, cuando finaliza la ejecución de la función, se elimina de la pila de llamadas y el contexto de ejecución en el que la variable x fue almacenada se destruye, lo mismo sucede con la función b().
  4. Pero aquí no podemos acceder a las variables x e y fuera del bloque, porque cuando la función termina su contexto de ejecución, también se elimina un contexto de ejecución y cuando la x desaparece, no está en ninguna parte de la memoria. Lo mismo para la función b(), no podemos acceder a var y fuera de esa función.

Producción: 

Ejemplo 2: En esta sección, discutiremos la función dentro de la función usando una string de alcance para acceder a la variable.

script.js

function a() {
  var x = 5,
    y = 6;
  console.log(x);
  
  function b() {
    var z = 7;
    console.log(y);
    console.log(z);
  }
  b();
}
a();

Explicación: 

  1. Aquí se crea un contexto de ejecución y luego la función a() obtiene espacio dentro de la memoria.
  2. Luego llamamos a la función a(), por lo que obtiene su propio contexto de ejecución.
  3. Nuevamente hay una variable x y la función b() que obtiene un espacio en la memoria y luego, durante la fase de ejecución del código, se imprime la variable x
  4. Tan pronto como el control pasa a llamar a la función b(), obtiene otro contexto de ejecución dentro del último contexto de ejecución y var y obtiene espacio en la memoria después de encontrar un console.log(x) pero x no está en el contexto de ejecución de la función b(), por lo que va al entorno léxico principal debido a la string de alcance y, con suerte, lo encuentra allí e imprime el valor de x,
  5. El var y es algo que está dentro de la función, por lo tanto, se imprime sin ningún esfuerzo adicional.  
     

Producción:

Ejemplo 3: En este caso, discutiremos la función de retorno de la función.

script.js

function a() {
  console.log("Hello, I am a function");
  
  function b() {
    console.log("Hello, I am an inner function");
  }
  return b;
}
const result = a();
result();

Explicación: 

  1. Se crea el contexto de ejecución global, el resultado y la función a() obtienen espacio de memoria asignado.
  2. Más tarde, en la llamada de la función a(), se crea otro contexto de ejecución y la función b() se asigna en ese espacio de memoria.
  3. Finalmente, después de imprimir una línea y devolver una función, a() finaliza su ejecución y la elimina de la pila de llamadas. Además, su contexto de ejecución se elimina.
  4. El resultado de la función a() se almacena en la variable const.
  5. Hemos llamado con éxito a la función result() y, como consecuencia, se ejecuta la funcionalidad dentro de la función b() que se devolvió.

Producción:

Ejemplo 4:  Aquí viene la parte de cierre. Digamos que no solo tiene un archivo console.log simple, sino que también se declaran algunas variables dentro de la función b(). Ahora, deténgase aquí y piense en el resultado del código.

script.js

function outer() {
  var x = 5;
  
  function inner() {
    console.log("Hello, I am an inner function");
    console.log(
      "I am accessing var x of my parent function"
      + " when that parent functions execution "
      + "context destroyed"
    );
    console.log(x);
  }
  return inner;
}
const result = outer();
result();

Explicación: 

  1. Contexto de ejecución global creado, resultado variable y función a() obtiene espacio dentro de la memoria.
  2. Durante la fase de ejecución del subproceso, en primer lugar, se llama a la función().
  3. Se crea un nuevo contexto de ejecución var x y la función b obtiene espacio en la memoria, y justo después devuelve la función b, lo que significa que tenemos el resultado como el código de la función, y ahora la función a() se elimina de la pila de llamadas también se destruye el contexto de ejecución.
  4. En la última línea, cuando llamamos a la función result(), comienza a ejecutar con éxito el código de función que contiene. Pero tan pronto como encuentra una variable que no está dentro del contexto de ejecución de esa función, intenta encontrar la var x en el ámbito externo, pero espere, no tenemos acceso al contexto de ejecución de la función a() , entonces, ¿cómo se ejecutará esta x?
  5. ¿Significa que var x se volverá indefinido? No, porque este es el concepto de cierre cada vez que exponemos o devolvemos la función desde otra función, no solo el código que se devuelve, sino que viene junto con una cosa especial llamada entorno léxico, que significa el entorno circundante de su función principal externa.
  6. Entonces, la función b() cuando regresa de la función, viene junto con el entorno léxico de su padre, por lo tanto, siempre tiene acceso a la referencia de la variable x. Tenga en cuenta la última línea » siempre tenga acceso a la referencia de variables «, no el valor.

Producción: 

Definición: Una clausura es la combinación de funciones agrupadas junto con su entorno léxico. El entorno léxico es la memoria de función local y la referencia al entorno léxico de un padre. En otras palabras, el cierre se crea cuando una función secundaria mantiene el entorno del ámbito principal incluso después de que la función principal ya se haya ejecutado. 

Esta referencia al entorno léxico de un padre es la razón por la cual las funciones de cierre tienen acceso a las variables de las funciones externas incluso si esas funciones no están en la pila de llamadas o podemos decir incluso si las funciones externas están cerradas.

Cómo se crean los cierres: el cierre se crea cada vez que se crea una función en el momento de la creación de otra función. En el ejemplo anterior, la función interior se crea en el momento de la creación de la función a(). 

Algunos casos de uso y ejemplos de cierres:

1. Privacidad: cada vez que tenemos que ocultar alguna funcionalidad o variable como encapsulación, envolvemos esa cosa dentro del entorno léxico del padre. 

Ejemplo: El siguiente ejemplo es una plantilla básica de encapsulación con cierres. 

Explicación: 

  1. Todo el contexto de ejecución global se creará en la fase de asignación de memoria, la función outside() y la función de cierre const obtendrán espacio en la memoria.
  2. En el momento de la ejecución del subproceso, el código interno de la función externa comenzará a ejecutarse porque es la primera línea del código.
  3. Ahora se creará otro contexto de ejecución y la const notAccessibleByAnyone obtendrá espacio y luego las funciones externas devolverán el código de la función interna y el contexto de ejecución desaparecerá. Observe que la variable const no está en el contexto de ejecución.
  4. Pero la función de cierre tiene la referencia a esa variable, por lo que este es todo el concepto de encapsulación con cierres.

script.js

function outer(secret) {
  const notAccessibleByAnyone = secret;
  
  return function inner() {
    console.log(notAccessibleByAnyone);
  };
}
  
const closureFunction = outer("Secret Data");
closureFunction();
console.log(notAccessibleByAnyone);

Salida: en la primera línea, los datos secretos se imprimen cuando el cierre accede a ellos y, en la siguiente línea, javascript arroja un error de referencia cuando alguien externo intenta acceder a eso.

2. Funciones parciales: cuando tenemos que crear algún tipo de funcionalidad en la que existe un patrón común, creamos una función principal que toma pocos parámetros y creamos una función interna que toma menos parámetros que la principal. Este concepto también se llama funciones de orden superior en las que devolvemos funciones de función. 

Ejemplo: El siguiente ejemplo proporciona una ilustración básica de cómo se ve esta función parcial. 

Explicación:

  1. Aquí hemos creado una función principal que recibe un multiplicador como parámetro y luego lo pasa a una función interna.
  2. Más tarde, cuando invocamos la función principal, devuelve el código de la función interna que siempre tiene acceso a ese multiplicador proporcionado durante la llamada a la función principal.
  3. La función multiplicar por 2() contiene una función que toma una array y ejecuta un bucle en ella, luego devuelve la array modificada en la que los elementos se multiplican por 2.
  4. La función multiplicar por 4() contiene una función que toma una array y ejecuta un bucle en ella, luego devuelve la array modificada en la que los elementos se multiplican por 4.

script.js

function partialApplication(multiplier) {
  return function inner(array) {
    let size = array.length;
    for (let i = 0; i < size; i++) {
      array[i] = array[i] * multiplier;
    }
    return array;
  };
}
  
const multiplyBy2 = partialApplication(2);
const arr1 = multiplyBy2([1, 2, 3]);
console.log(arr1);
  
const multiplyBy4 = partialApplication(4);
const arr2 = multiplyBy4([1, 2, 3]);
console.log(arr2);

Producción:

Publicación traducida automáticamente

Artículo escrito por mrtwinklesharma y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *