Cl

Clausuras en JavaScript

1 ejercicio

Acerca de Clausuras

Los closures son un patrón de programación en JavaScript que permite usar variables de un scope léxico externo dentro de un bloque de código anidado. JavaScript admite los closures de forma transparente y a menudo se usan sin saber qué son.

// Top-level declarations are global-scope
const dozen = 12;

// Functions create a new function-scope and block-scope.
// Referencing the outer variable here is a closure.
function nDozen(n) {
  return dozen * n;
}

El nombre closure deriva históricamente del cálculo λ y fue popularizado por Scheme (fuente) para referirse a las vinculaciones abiertas y cerradas de variables de una función.

Motivos para usar closures en JavaScript

  1. Privacidad de datos / Encapsulación de datos

    • A diferencia de otros lenguajes, en 2020 no había forma de especificar variables privadas. Así que los closures pueden usarse para emular de forma efectiva variables privadas (existía una propuesta para introducir una notación de variables privadas que puede que ya sea estándar cuando leas esto).
    // Consider this function:
    
    const saveNumber = (number) => {
      // The saved value is unaccessible by the outer lexical scope.
      const value = number;
    
      // We can provide access to the primitive value with a function, but the original will never change
      return () => value;
    };
    
    // Attempting to set the variable outside of its lexical scope results in an error
    value = 42;
    
  2. Aplicación parcial

    • Las funciones pueden devolver funciones y, cuando una función devuelta usa el argumento de la función que la creó, es un ejemplo de cómo usar un closure para llevar a cabo una aplicación parcial. A veces, esto se llama hacer currying a una función.
    // The arguments are applied one-by-one, using closures to arrive at the final result
    function partialBuildUri(scheme) {
      return function (domain) {
        return function (path) {
          return `${scheme}://${domain}/${path}`;
        };
      };
    }
    
    // A function could apply them all at once.
    function buildUri(scheme, domain, path) {
      return partialBuildUri(scheme)(domain)(path);
    }
    
    // Or apply a few to create a flexible system of functions for reuse.
    function buildHttpsExercismUri(path) {
      return partialBuildUri('https')('exercism.org');
    }
    

Scope léxico de las variables

En cualquier lenguaje de programación, los closures están estrechamente relacionados con el scope de las variables. En JavaScript hay tres tipos de scope léxico para las variables:

  1. Scope global: son las variables que se declaran fuera de las funciones y los bloques.
  2. Scope de función: son las variables declaradas dentro de una función con la palabra clave var.
  3. Scope de bloque: son las variables declaradas dentro de un bloque ({...}) con las palabras clave let y const.

En la práctica, un closure suele producirse cuando una función (o un bloque) usa una variable de un scope externo. En JavaScript, esto se admite de forma transparente, por lo que a menudo se usan closures sin mencionarlos de forma explícita.

Scope de función

La palabra clave var define una variable con scope de función. Esto significa que las variables definidas con var están disponibles en cualquier parte de la función en la que se definen y en cualquier scope léxico anidado (ya sea de función o de bloque).

{
  var five = 5;
}

// This will succeed because 'five' exists in the same scope
var tenTimes = five * 10;

Scope de bloque

Las palabras clave let y const definen variables con scope de bloque. let define una variable mutable, cuyo valor puede asignarse varias veces y puede definirse sin inicializarse. const define una variable que debe inicializarse en el momento de definirla, y su valor no puede cambiar después. Si la variable const se asigna a un valor primitivo, no puede mutarse. Si la variable const se asigna a un valor de referencia, como un array o un objeto, la referencia no puede cambiar, pero el contenido del array sí puede mutarse.

Fíjate en este bloque:

// 'two' is defined at the top-level, therefore available to the global-scope
const two = 2;

{
  // This succeeds, as 'two' is available from the outer lexical scope
  const oneLess = two - 1;
}

// This throws an error because oneLess is not defined outside of the previous inner lexical scope
console.log(oneLess);

Aunque podemos usar llaves ({...}) de forma arbitraria para crear bloques, lo más habitual es encontrarlas rodeando condicionales, funciones y clases.

const PI = 3.14;

// This function uses a closure for the value of PI (π)
function areaOfCircle(radius) {
  return PI * radius * radius;
}
Editar en GitHub El enlace se abre en una nueva ventana o pestaña

Aprende Clausuras