Ir para o conteúdo
O que são encerramentos em JavaScript?

O que são encerramentos em JavaScript?

Um fechamento JavaScript é uma função que lembra o ambiente em que foi criado. Podemos pensar nele como um objeto com um método e variáveis privadas.

7min read

Os encerramentos JavaScript são um tipo especial de objeto que contém a função e o escopo local da função com todas as variáveis (ambiente) quando o encerramento foi criado.

O fechamento JavaScript é uma função que lembra o ambiente em que foi criado

Para entender os encerramentos, primeiro precisamos entender o SCOPING no JavaScript.  Podemos criar uma variável ou uma função em três níveis de escopo,

  1. Global scope
  2. Função ou escopo local
  3. Escopo lexical

Scopes in JavaScript

Assim que criamos uma variável, ela está em um escopo global. Então, se criamos uma variável que não está dentro de nenhuma função, ela está em um escopo global.

var foo = "foo";
console.log(foo);

Se algo está em um escopo global, podemos acessá-lo em qualquer lugar – o que o torna nosso amigo e inimigo ao mesmo tempo. Colocar tudo em um escopo global nunca é uma boa ideia, porque pode causar conflitos de namespace entre outros problemas. Se algo estiver em um escopo global, ele pode ser acessado de qualquer lugar e, se houver variáveis com o mesmo nome em uma função, isso pode causar conflitos.

Qualquer variável ou função que não esteja dentro de um escopo global está dentro de um escopo funcional ou local. Considere a lista abaixo:

function foo() {
  var doo = "doo";
  console.log(doo);
}

foo();

Criamos uma variável doo que está dentro do escopo da função foo. O tempo de vida da variável doo é local para a função foo e não pode ser acessado fora dessa função. Isso é chamado de escopo local em JavaScript.  Vamos considerar a listagem de código mostrada como no diagrama abaixo:

Vamos considerar a listagem de código mostrada como no diagrama

Aqui criamos uma variável na função foo com o mesmo nome de uma variável do escopo global, então agora temos duas variáveis. Devemos ter em mente que essas variáveis são duas variáveis diferentes com seus respectivos tempos de vida. Fora da função, a variável doo com valor a é acessível, no entanto, dentro da função foo, existe a variável doo com vale doo. Para referência, o código acima é fornecido abaixo:

var doo = "a";
function foo() {
  var doo = "doo";
  console.log(doo); //print doo
}

foo();
console.log(doo); //print a

Let us tweak the above code snippet a bit as shown in listing below:

var doo = "a";
function foo() {
  doo = "doo";
  console.log(doo); //print doo
}

foo();
console.log(doo); //print doo

Agora não temos dois escopos para a variável doo. Dentro da função foo, a variável doo criada no escopo global está sendo modificada.  Não estamos recriando a variável doo dentro de foo, mas modificando a variável existente doo do escopo global.

Não estamos recriando a variável doo dentro de foo, mas modificando a variável existente doo do escopo global

Ao criar a variável no escopo local ou funcional, devemos usar a palavra-chave var para criar a variável.  Caso contrário, a variável será criada no escopo global ou, se a variável já existir no escopo global, ela será modificada.

Em JavaScript, podemos ter uma função dentro de uma função. Pode haver funções aninhadas de qualquer nível, o que significa que podemos ter qualquer número de funções aninhadas umas dentro das outras.  Considere a lista abaixo:

function foo() {
  var f = "foo";

  function doo() {
    console.log(f);
  }

  doo();
}

foo(); //print foo

Essencialmente, no trecho acima, temos uma função doo que é criada dentro da função foo, e não tem nenhuma de suas próprias variáveis. A função foo cria uma variável local e ela pode ser acessada dentro da função doo. A função doo é a função interna da função foo e pode acessar as variáveis da função foo. Além disso, a função doo pode ser chamada dentro do corpo da função foo. A função doo pode acessar as variáveis declaradas na função pai e isso se deve ao escopo léxico do JavaScript.

Existem dois níveis de escopo aqui:

  1. Parent function foo scope
  2. Child function doo scope
Existem dois níveis de escopo aqui

As variáveis criadas dentro da função doo têm acesso a tudo o que é criado dentro do escopo da função foo devido ao escopo lexical do JavaScript. No entanto, a função foo não pode acessar as variáveis da função doo.

function foo não pode acessar as variáveis da função doo

Closures in JavaScript

Vamos começar a entender os encerramentos em JavaScript com um exemplo. Considere a listagem conforme mostrado abaixo. Em vez de chamar a função doo dentro do corpo da função foo, estamos retornando a função doo da função foo.

function foo() {
  var f = "foo";
  function doo() {
    console.log(f);
  }
  return doo;
}
var afunct = foo();
afunct();

 

Na lista acima:

  1. function foo está retornando outra função doo
  2. função doo não tem nenhuma de suas próprias variáveis
  3. Devido ao escopo lexical, a função DOO é capaz de acessar a variável da função pai FOO
  4. function foo é chamada e atribuída a uma variável afunct
  5. então afunct é chamado como uma função e imprime a string "foo"
 function foo

Surpreendentemente, a saída do trecho de código acima é a string "foo". Agora podemos ficar confusos: como a variável f é acessada fora da função foo? Normalmente, as variáveis locais dentro de uma função existem apenas durante a execução dessa função. Portanto, idealmente após a execução de foo, a variável f não deve mais estar acessível. Mas em JavaScript podemos acessá-lo, porque afunct se tornou um fechamento JavaScript. O encerramento afunct tem informações sobre a função doo e todas as variáveis de escopo local da função doo no momento da criação do encerramento afunct.

Em caso de fechamento, a função interna mantém as referências do escopo da função externa. Então, em encerramentos:

  • A função Inner mantém a referência de seu escopo de função externa. Nesse caso, a função doo mantém a referência da função foo scope.
  • A função doo pode acessar variáveis da referência do escopo do alimento da função a qualquer momento, mesmo que a função externa foo tenha terminado de ser executada.
  • O JavaScript mantém a referência de escopo da função externa (foo neste caso) e suas variáveis na memória até que exista uma função interna e faça referência a ela. Nesse caso, o escopo e as variáveis da função foo serão mantidos na memória pelo JavaScript, até que a função doo exista.

Para entender melhor o fechamento, vamos discutir mais um exemplo:

function add(num1) {
  function addintern(num2) {
    return num1 + num2;
  }

  return addintern;
}

var sum9 = add(7)(2);
console.log(sum9);
var sum99 = add(77)(22);
console.log(sum99);

Temos dois fechamentos aqui, SUM9 e SUM99.

Quando o encerramento sum9 foi criado, no escopo local da função, o valor addintern de num1 era 7, e o JavaScript lembra esse valor ao criar encerramentos sum9.

O mesmo no caso de sum99 de encerramento, no escopo local da função, o valor addintern de num1 era 7, e o JavaScript lembra esse valor ao criar sum99 de encerramento.  Como esperado, a saída seria 9 e 99.

Podemos pensar em um fechamento como um objeto com variáveis privadas e um método. Os encerramentos nos permitem anexar dados com a função que funciona nesses dados.  Assim, um fechamento pode ser definido com as seguintes características:

  • It is an object
  • Ele contém uma função
  • Ele lembra os dados associados à função, incluindo as variáveis do escopo local da função quando o fechamento foi criado
  • Para criar um fechamento, a função deve retornar outra referência de função

Finalmente, podemos definir um fechamento:

"JavaScript Closure é um tipo especial de objeto que contém uma função e o ambiente no qual a função foi criada. Aqui, ambiente representa o escopo local da função e todas as suas variáveis no momento da criação do fechamento."

Conclusão

Neste post, aprendemos sobre Closures em JavaScript. Espero que você ache útil. Obrigado por ler!

[Atualizado em 22/03/2019]

Solicite uma demonstração