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 escopo lexical externo dentro de um bloco de código aninhado. O JavaScript oferece suporte a closures de forma transparente, e muitas vezes eles são usados sem que se saiba 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 lambda e foi popularizado pelo Scheme (fonte) para se referir às ligações abertas e fechadas de variáveis de uma função.

Por que usar closures em JavaScript

  1. Privacidade de dados / Encapsulamento de dados

    • Ao contrário de outras linguagens, em 2020 não havia como especificar variáveis privadas. Por isso, dá para usar closures para emular de forma eficaz variáveis privadas (havia uma proposta de introduzir uma notação para variáveis privadas, que pode já ser padrão quando você ler 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 retornar funções e, quando uma função retornada usa o argumento da função que a criou, isso é um exemplo de uso de um closure para fazer aplicação parcial. Às vezes, isso é chamado de 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');
    }
    

Escopo lexical para variáveis

Em qualquer linguagem de programação, os closures estão intimamente relacionados ao escopo de variáveis. Em JavaScript, existem 3 tipos de escopo lexical para variáveis:

  1. Escopo global: são variáveis declaradas fora de funções e de blocos.
  2. Escopo de função: são variáveis declaradas dentro de uma função com a palavra-chave var.
  3. Escopo 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 escopo externo. Em JavaScript, isso é suportado de forma transparente, então os closures costumam ser usados sem uma referência específica.

Escopo de função

A palavra-chave var define uma variável com escopo de função. Isso significa que as variáveis definidas com var ficam disponíveis em qualquer lugar da função em que são definidas e em qualquer escopo lexical aninhado (seja de função ou de bloco).

{
  var five = 5;
}

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

Escopo de bloco

As palavras-chave let e const definem variáveis com escopo de bloco. let define uma variável mutável, cujo valor pode ser definido várias vezes e que pode ser declarada sem ser inicializada. const define uma variável que precisa ser inicializada no momento em que é definida, e cujo valor não pode mais mudar. Se a variável const receber um valor primitivo, ela não pode ser modificada. Se a variável const receber um valor de referência, como um array ou objeto, a referência não pode mudar, mas o conteúdo do array pode ser modificado.

Considere 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 chaves ({...}) livremente para criar blocos, elas aparecem com mais frequência em volta 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 O link abre em uma nova janela ou aba

Aprenda Closures