Closures em JavaScript

Closures em JavaScript

Este artigo explica closures em JavaScript.

YouTube Video

Closures em JavaScript

No JavaScript, um 'closure' é um dos conceitos mais importantes e poderosos. Ao compreender closures, você obtém conhecimento útil em várias situações, como o comportamento e escopo das funções, além do processamento assíncrono do JavaScript e manipulação de eventos. Aqui, explicaremos em detalhes tudo, desde a definição básica de closures até exemplos concretos e suas aplicações.

O que é um Closure?

Um closure refere-se ao mecanismo pelo qual uma função pode acessar variáveis em seu escopo de criação mesmo quando chamada fora desse escopo. Ao utilizar closures, torna-se possível que funções continuem a 'lembrar' variáveis externas.

Closures consistem dos seguintes dois elementos.

  1. Definição da função (a própria função)

  2. Escopo em que a função é definida (variáveis e outras funções fora da própria função)

Em JavaScript, closures são possíveis porque as funções têm a capacidade de acessar variáveis dentro do escopo em que foram criadas.

Exemplo Básico

Primeiro, vamos ver um exemplo básico de closure. No código a seguir, outerFunction retorna uma função chamada innerFunction. O ponto importante é que innerFunction pode acessar a variável count definida dentro do escopo de outerFunction.

 1function outerFunction() {
 2    let count = 0;
 3
 4    function innerFunction() {
 5        count++;
 6        console.log(`Current count: ${count}`);
 7    }
 8
 9    return innerFunction;
10}
11
12const counter = outerFunction();
13counter(); // Current count: 1
14counter(); // Current count: 2
15counter(); // Current count: 3

Como os Closures Funcionam

Como visto no exemplo acima, count é mantido por innerFunction mesmo após a execução de outerFunction. innerFunction pode continuar acessando o escopo de outerFunction e, portanto, count é atualizado dentro de innerFunction. Esse é o mecanismo básico de um closure.

É innerFunction que é atribuída à variável counter, e podemos ver que o estado de count é preservado mesmo após outerFunction já ter sido executada. Isso acontece porque o JavaScript continuamente 'lembra' do escopo no momento da definição da função.

Aplicação: Closures como Variáveis Privadas

Closures podem ser usados como 'variáveis privadas' em programação orientada a objetos. Normalmente, em JavaScript, as propriedades de objetos são acessíveis diretamente de fora, mas ao usar closures, é possível impedir a manipulação direta das variáveis dentro do escopo da função a partir do exterior.

No exemplo a seguir, a função createCounter usa um closure para criar um contador e retorna um contador com uma variável privada count.

 1function createCounter() {
 2    let count = 0;
 3
 4    return {
 5        increment: function() {
 6            count++;
 7            console.log(`Count: ${count}`);
 8        },
 9        decrement: function() {
10            count--;
11            console.log(`Count: ${count}`);
12        },
13        getCount: function() {
14            return count;
15        }
16    };
17}
18
19const myCounter = createCounter();
20myCounter.increment(); // Count: 1
21myCounter.increment(); // Count: 2
22myCounter.decrement(); // Count: 1
23console.log(myCounter.getCount()); // 1

Neste exemplo, count está no escopo da função createCounter, portanto não pode ser acessada diretamente de fora. No entanto, pode ser manipulada através dos métodos increment e decrement. Dessa forma, usando closures, é possível incorporar o conceito de variáveis privadas em JavaScript.

Exemplos Práticos de Closures

Combinação com Funções de Callback

Closures são frequentemente usados em conjunto com funções de callback para gerenciar o processamento assíncrono. Por exemplo, vamos considerar um exemplo usando um temporizador.

 1function startTimer(duration) {
 2    let timeLeft = duration;
 3
 4    function countdown() {
 5        console.log(`Time left: ${timeLeft} seconds`);
 6        timeLeft--;
 7
 8        if (timeLeft >= 0) {
 9            setTimeout(countdown, 1000);
10        }
11    }
12
13    countdown();
14}
15
16startTimer(5);
17// Time left: 5 seconds
18// Time left: 4 seconds
19// Time left: 3 seconds
20// Time left: 2 seconds
21// Time left: 1 second
22// Time left: 0 seconds

Neste exemplo, a função countdown acessa a variável timeLeft dentro do escopo de startTimer. Dessa forma, closures são muito úteis para processamento assíncrono como temporizadores, pois mantêm o estado das variáveis ao longo do tempo.

Manipuladores de Evento

Closures também são convenientes para configurar manipuladores de eventos. No exemplo a seguir, um closure é usado para registrar o número de vezes que um botão é clicado.

 1function setupClickCounter(buttonId) {
 2    let clickCount = 0;
 3
 4    const button = document.getElementById(buttonId);
 5    button.addEventListener('click', function() {
 6        clickCount++;
 7        console.log(`Button clicked ${clickCount} times`);
 8    });
 9}
10
11setupClickCounter('myButton');

Neste caso, clickCount aumenta a cada clique, e seu valor é preservado. Usando closures, você pode atribuir um contador independente para cada botão.

Conclusão

Closures são um conceito que simboliza a flexibilidade e o poder do JavaScript. Eles mantêm variáveis fechadas dentro do escopo de uma função e permitem operações nessas variáveis por meio de funções acessíveis externamente. Ao entender e utilizar esse mecanismo, você pode adquirir técnicas mais avançadas de programação em JavaScript.

Closures são aplicados em várias situações, desde manipulação de eventos e processamento assíncrono até mesmo em pseudo-implementações de programação orientada a objetos.

Você pode acompanhar o artigo acima usando o Visual Studio Code em nosso canal do YouTube. Por favor, confira também o canal do YouTube.

YouTube Video