Cl

Closures em JavaScript

1 exercício

Sobre Closures

Closures são um padrão de programação em JavaScript que permite usar variáveis de um âmbito lexical exterior dentro de um bloco de código aninhado. O JavaScript suporta closures de forma transparente, e são muitas vezes usados sem se saber o que são.

// 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;
}

O nome closure deriva historicamente do cálculo λ e foi popularizado pelo Scheme (fonte) para designar as ligações de variáveis abertas e fechadas de uma função.

Razões para usar closures em JavaScript

  1. Privacidade de dados / Encapsulamento de dados

    • Ao contrário de outras linguagens, em 2020 não havia forma de especificar variáveis privadas. Por isso, os closures podem ser usados para emular eficazmente variáveis privadas (existia uma proposta para introduzir uma notação de variáveis privadas, que pode já ser padrão quando leres isto).
    // 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. Aplicação parcial

    • As funções podem devolver funções e, quando uma função devolvida usa o argumento da função que a criou, isto é um exemplo de usar um closure para fazer aplicação parcial. Por vezes, a isto chama-se fazer currying de uma função.
    // 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');
    }
    

Âmbito lexical das variáveis

Em qualquer linguagem de programação, os closures estão estreitamente relacionados com o âmbito das variáveis. Em JavaScript, há 3 tipos de âmbito lexical para variáveis:

  1. Âmbito global: São variáveis declaradas fora de funções e de blocos.
  2. Âmbito de função: São variáveis declaradas dentro de uma função com a palavra-chave var.
  3. Âmbito de bloco: São variáveis declaradas dentro de um bloco ({...}) com as palavras-chave let e const.

Na prática, um closure ocorre muitas vezes quando uma função (ou bloco) usa uma variável de um âmbito exterior. Em JavaScript, isto é suportado de forma transparente, pelo que os closures são frequentemente usados sem referência específica.

Âmbito de função

A palavra-chave var define uma variável de âmbito de função. Isto significa que as variáveis definidas por var estão disponíveis em qualquer ponto da função onde são definidas e em qualquer âmbito lexical aninhado (quer seja de função, quer de bloco).

{
  var five = 5;
}

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

Âmbito de bloco

As palavras-chave let e const definem variáveis de âmbito de bloco. let define uma variável mutável, cujo valor pode ser definido várias vezes e que pode ser definida mas não inicializada. const define uma variável que tem de ser inicializada quando é definida, e cujo valor não pode depois mudar. Se a variável const for definida com um valor primitivo, não pode ser alterada. Se a variável const for definida com um valor de referência, como um array ou um objeto, a referência não pode mudar, mas o conteúdo do array pode ser alterado.

Considera este bloco:

// '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);

Embora possamos usar chavetas ({...}) arbitrariamente para criar blocos, é mais frequente encontrá-las em torno de condicionais, funções e classes.

const PI = 3.14;

// This function uses a closure for the value of PI (π)
function areaOfCircle(radius) {
  return PI * radius * radius;
}
Editar via GitHub A ligação abre numa nova janela ou separador

Aprende Closures