W3docs

Closures

Aprenda closures em JavaScript: como uma função memoriza seu escopo lexical, com exemplos de privacidade de dados, fábricas de funções, o padrão módulo e armadilhas comuns em loops.

Entendendo Closures em JavaScript

Em JavaScript, closures são um conceito fundamental e poderoso que permite a criação de funções com dados preservados de seu ambiente lexical. Este guia oferece uma exploração aprofundada de closures, com explicações detalhadas e exemplos práticos de código para solidificar o seu entendimento.

O Que é uma Closure?

Uma closure é a combinação de uma função e o ambiente lexical dentro do qual essa função foi declarada. Esse ambiente consiste em quaisquer variáveis locais que estavam no escopo no momento em que a closure foi criada.

Exemplo de uma Closure

Considere o seguinte exemplo:

javascript— editable

Neste exemplo, greetByName é uma closure. Ela captura as variáveis greeting e name do escopo da função greet.

Quando a função greet é chamada com o argumento 'John', ela cria uma variável local greeting e uma função greetByName. A função greet então retorna a função greetByName. Mesmo após a função greet ter terminado de executar, a função greetByName retornada ainda tem acesso às variáveis greeting e name. Isso ocorre porque greetByName é uma closure, o que significa que ela memoriza o ambiente onde foi criada.

Por Que Usar Closures?

Closures permitem o encapsulamento de dados e a criação de variáveis privadas. Elas permitem que funções retenham acesso a variáveis de seu escopo externo (envolvente), mesmo após esse escopo ter terminado de executar.

Criando uma Closure Básica

Considere o seguinte exemplo:

javascript— editable

Quando outerFunction é invocada, ela cria outerVariable e innerFunction. Em seguida, retorna innerFunction. A innerFunction retornada mantém acesso a outerVariable mesmo após outerFunction ter terminado de executar.

Casos de Uso Práticos de Closures

Privacidade de Dados

Closures podem ser usadas para criar dados privados que não podem ser acessados do escopo global.

javascript— editable

Neste exemplo, count é uma variável privada que só pode ser acessada e modificada pela função anônima retornada. Cada vez que a função é chamada, ela incrementa e retorna o count atualizado.

Um ponto sutil, mas importante: cada chamada a createCounter produz sua própria closure independente com seu próprio count privado. Dois contadores criados separadamente não compartilham estado:

javascript— editable

Cada invocação da função externa cria um ambiente lexical totalmente novo, portanto a e b incrementam variáveis separadas.

Fábricas de Funções

Closures podem ser usadas para criar fábricas de funções que geram funções especializadas.

javascript— editable

Aqui, createMultiplier gera funções que multiplicam um dado número por um multiplier especificado. Cada função retornada mantém acesso ao valor de multiplier por meio da closure.

Closures em Loops

Closures podem ser complicadas quando usadas dentro de loops. Considere o seguinte exemplo:

javascript— editable

Neste código, o loop é concluído e, em seguida, todos os três callbacks do setTimeout são executados, registrando o valor de i, que é 4 após o término do loop. Isso acontece porque var tem escopo de função.

Para corrigir isso, use let, que tem escopo de bloco:

javascript— editable
Informação

Sempre use let ou const em vez de var para evitar problemas de escopo e garantir que as variáveis tenham o escopo apropriado.

Por Que a Versão com let Funciona

A chave é que let cria um novo binding de i para cada iteração do loop. Cada callback do setTimeout fecha sobre um i diferente, então ele memoriza o valor que existia durante sua própria iteração. Com var, há apenas um i compartilhado por todos os callbacks e, quando os temporizadores disparam, o loop já o incrementou para 4.

Antes de let existir, a solução clássica era criar um escopo fresco por iteração com uma IIFE:

javascript— editable

A função invocada imediatamente copia o valor atual de i para seu próprio parâmetro captured, dando a cada callback uma cópia privada sobre a qual fechar.

Closures e Gerenciamento de Memória

Closures podem causar vazamentos de memória se não forem usadas adequadamente. Como closures mantêm referências ao seu escopo externo, variáveis não utilizadas podem persistir na memória por mais tempo do que o necessário.

Aviso

Esteja atento ao uso de memória ao criar closures. Certifique-se de que as closures não capturem desnecessariamente objetos grandes ou elementos DOM para evitar vazamentos de memória.

Padrões Avançados de Closure

Padrão Módulo

O padrão módulo utiliza closures para criar membros privados e públicos dentro de uma função.

javascript— editable

Neste exemplo, CounterModule é uma expressão de função invocada imediatamente (IIFE) que retorna um object com métodos públicos (increment e reset). A variável count permanece privada e não pode ser acessada diretamente.

Closures em Frameworks Modernos de JavaScript

Closures são particularmente importantes em frameworks modernos de JavaScript como o React. No React, closures ajudam a gerenciar estado e efeitos colaterais dentro de componentes funcionais.

import React, { useState } from 'react';

function Counter() {
    const [count, setCount] = useState(0);

    function increment() {
        setCount(prevCount => prevCount + 1);
    }

    return (
        <div>
            <p>{count}</p>
            <button onClick={increment}>Increment</button>
        </div>
    );
}

Neste exemplo, useState é usado para criar uma variável de estado count e uma função setCount para atualizá-la. A função increment forma uma closure, capturando setCount e count do escopo do componente. Observação: No React, closures podem às vezes capturar estado desatualizado se as dependências não forem gerenciadas adequadamente em hooks como useEffect.

Informação

Pratique criar e usar closures em diferentes contextos para compreender plenamente seu potencial e suas limitações. Elas são uma ferramenta essencial no arsenal de qualquer desenvolvedor JavaScript.

Boas Práticas para Usar Closures

  1. Limite o Escopo: Evite criar closures dentro de loops ou estruturas profundamente aninhadas, a menos que necessário, para prevenir vazamentos de memória e problemas de desempenho.
  2. Minimize as Variáveis Capturadas: Capture apenas as variáveis que você precisa dentro da closure para reduzir o consumo de memória.
  3. Evite Capturas Grandes: Não capture objetos grandes ou elementos DOM dentro de closures para evitar retenção desnecessária de memória.
  4. Use let e const: Sempre prefira let ou const a var para garantir o escopo adequado e evitar comportamento indesejado.
  5. Faça Limpeza: Sempre que possível, limpe as referências capturadas por closures para evitar vazamentos de memória, especialmente em aplicações de longa duração.

Conclusão

Closures são um recurso poderoso em JavaScript, permitindo privacidade de dados, fábricas de funções e padrões avançados como módulos. Compreender e utilizar closures de forma eficaz pode levar a um código mais robusto e sustentável. Ao dominar closures, você aprimorará sua capacidade de escrever código JavaScript eficiente, seguro e limpo.

Prática

Prática
O que são closures em JavaScript?
O que são closures em JavaScript?
Prática
Por que um loop 'for' usando 'var' com setTimeout registra o mesmo valor final três vezes, enquanto 'let' registra 1, 2, 3?
Por que um loop 'for' usando 'var' com setTimeout registra o mesmo valor final três vezes, enquanto 'let' registra 1, 2, 3?
Was this page helpful?