Fechamentos de JavaScript

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 da greeting()função.
  • A variável messageé uma variável local acessível apenas dentro da greeting()função.

Se você tentar acessar a messagevariá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 messagee 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 messagevariá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 variableLinguagem 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 à hivariá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 messagevariável não estará mais acessível.

Neste caso, executamos a hi()função que faz referência à sayHi()função, a messagevariá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 JohnLinguagem de código:  JavaScript  ( javascript )

A greeting()função recebe um argumento nomeado messagee 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 Hie 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 messageis Hi, enquanto no sayHello()encerramento o messageis 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):4Linguagem de código:  CSS  ( css )

O código mostra a mesma mensagem.

O que queríamos fazer no loop é copiar o valor de   iem 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 letpalavra-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):3Linguagem de código:  CSS  ( css )

2) Usando a palavra-chave let no ES6

No ES6, você pode usar a letpalavra-chave para declarar uma variável com escopo de bloco.

Se você usar a letpalavra-chave no for-loop , um novo escopo léxico será criado em cada iteração. Em outras palavras, você terá uma nova indexvariável em cada iteração.

Além disso, o novo escopo lexical é encadeado ao escopo anterior para que o valor anterior de indexseja 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):3Linguagem 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.

Deixe um comentário

O seu endereço de email não será publicado. Campos obrigatórios marcados com *