Resumo : neste tutorial, você aprenderá sobre encerramentos JavaScript e como usá-los em seu código de forma mais eficaz.
Introdução aos fechamentos JavaScript
Em JavaScript, um encerramento é uma função que faz referência a variáveis no escopo externo a partir de seu escopo interno. O fechamento preserva o escopo externo dentro do escopo interno.
Para entender os fechamentos, você precisa primeiro saber como funciona o escopo lexical.
Escopo léxico
O escopo léxico define o escopo de uma variável pela posição dessa variável declarada no código-fonte. Por exemplo:
let name = 'John';
function greeting() {
let message = 'Hi';
console.log(message + ' '+ name);
}
Linguagem de código: JavaScript ( javascript )
Neste exemplo:
- A variável
name
é uma variável global. É acessível de qualquer lugar, inclusive dentro dagreeting()
função. - A variável
message
é uma variável local acessível apenas dentro dagreeting()
função.
Se você tentar acessar a message
variável fora da greeting()
função, receberá um erro.
Portanto, o mecanismo JavaScript usa o escopo para gerenciar a acessibilidade da variável.
De acordo com o escopo léxico, os escopos podem ser aninhados e a função interna pode acessar as variáveis declaradas em seu escopo externo. Por exemplo:
function greeting() {
let message = 'Hi';
function sayHi() {
console.log(message);
}
sayHi();
}
greeting();
Linguagem de código: JavaScript ( javascript )
A greeting()
função cria uma variável local chamada message
e uma função chamada sayHi()
.
The sayHi()
é a função interna que está disponível apenas dentro do corpo da greeting()
função.
A sayHi()
função pode acessar as variáveis da função externa, como a message
variável da greeting()
função.
Dentro da greeting()
função, chamamos a sayHi()
função para exibir a mensagem Hi
.
Fechamentos de JavaScript
Vamos modificar a greeting()
função:
function greeting() {
let message = 'Hi';
function sayHi() {
console.log(message);
}
return sayHi;
}
let hi = greeting();
hi(); // still can access the message variable
Linguagem de código: JavaScript ( javascript )
Agora, em vez de executar a sayHi()
função dentro da greeting()
função, a greeting()
função retorna o sayHi()
objeto de função.
Observe que as funções são cidadãos de primeira classe em JavaScript , portanto, você pode retornar uma função de outra função.
Fora da greeting()
função, atribuímos à hi
variável o valor retornado pela greeting()
função, que é uma referência à sayHi()
função.
Então executamos a sayHi()
função usando a referência dessa função: hi()
. Se você executar o código, obterá o mesmo efeito acima.
Porém, o interessante aqui é que, normalmente, uma variável local só existe durante a execução da função.
Isso significa que quando a greeting()
execução da função for concluída, a message
variável não estará mais acessível.
Neste caso, executamos a hi()
função que faz referência à sayHi()
função, a message
variável ainda existe.
A mágica disso é o encerramento . Em outras palavras, a sayHi()
função é um fechamento.
Um encerramento é uma função que preserva o escopo externo em seu escopo interno.
Mais exemplos de fechamento de JavaScript
O exemplo a seguir ilustra um exemplo mais prático de fechamento.
function greeting(message) {
return function(name){
return message + ' ' + name;
}
}
let sayHi = greeting('Hi');
let sayHello = greeting('Hello');
console.log(sayHi('John')); // Hi John
console.log(sayHello('John')); // Hello John
Linguagem de código: JavaScript ( javascript )
A greeting()
função recebe um argumento nomeado message
e retorna uma função que aceita um único argumento chamado name
.
A função return retorna uma mensagem de saudação que é uma combinação das variáveis message
e name
.
A greeting()
função se comporta como uma fábrica de funções. Ele cria sayHi()
e sayHello()
funciona com as respectivas mensagens Hi
e arquivos Hello
.
Os sayHi()
e sayHello()
são fechamentos. Eles compartilham o mesmo corpo de função, mas armazenam escopos diferentes.
No sayHi()
encerramento, o message
is Hi
, enquanto no sayHello()
encerramento o message
is Hello
.
Fechamentos JavaScript em um loop
Considere o seguinte exemplo:
for (var index = 1; index <= 3; index++) {
setTimeout(function () {
console.log('after ' + index + ' second(s):' + index);
}, index * 1000);
}
Linguagem de código: JavaScript ( javascript )
Saída
after 4 second(s):4
after 4 second(s):4
after 4 second(s):4
Linguagem de código: CSS ( css )
O código mostra a mesma mensagem.
O que queríamos fazer no loop é copiar o valor de i
em cada iteração no momento da iteração para exibir uma mensagem após 1, 2 e 3 segundos.
A razão pela qual você vê a mesma mensagem após 4 segundos é que o retorno de chamada passou para o setTimeout()
fechamento. Ele lembra o valor da i
última iteração do loop, que é 4.
Além disso, todos os três fechamentos criados pelo loop for compartilham o mesmo escopo global e acessam o mesmo valor de i
.
Para corrigir esse problema, você precisa criar um novo escopo de fechamento em cada iteração do loop.
Existem duas soluções populares: IIFE e let
palavra-chave.
1) Usando a solução IIFE
Nesta solução, você usa uma expressão de função invocada imediatamente (também conhecida como IIFE) porque um IIFE cria um novo escopo declarando uma função e executando-a imediatamente.
for (var index = 1; index <= 3; index++) {
(function (index) {
setTimeout(function () {
console.log('after ' + index + ' second(s):' + index);
}, index * 1000);
})(index);
}
Linguagem de código: JavaScript ( javascript )
Saída
after 1 second(s):1
after 2 second(s):2
after 3 second(s):3
Linguagem de código: CSS ( css )
2) Usando a palavra-chave let no ES6
No ES6, você pode usar a let
palavra-chave para declarar uma variável com escopo de bloco.
Se você usar a let
palavra-chave no for-loop , um novo escopo léxico será criado em cada iteração. Em outras palavras, você terá uma nova index
variável em cada iteração.
Além disso, o novo escopo lexical é encadeado ao escopo anterior para que o valor anterior de index
seja copiado do escopo anterior para o novo.
for (let index = 1; index <= 3; index++) {
setTimeout(function () {
console.log('after ' + index + ' second(s):' + index);
}, index * 1000);
}
Linguagem de código: JavaScript ( javascript )
Saída
after 1 second(s):1
after 2 second(s):2
after 3 second(s):3
Linguagem de código: CSS ( css )
Resumo
- O escopo léxico descreve como o mecanismo JavaScript usa a localização da variável no código para determinar onde essa variável está disponível.
- Um encerramento é uma combinação de uma função e sua capacidade de lembrar variáveis no escopo externo.