Desvendando Closure Javascript
Taise Soares
Posted on April 12, 2023
Olá pessoas, bora falar um pouco sobre Closure Javascript, o que são? Do que se alimentam? E como usá-las? 🤔
Se você já ouviu falar, mas nunca entendeu direito o que é, ou se você é novo no mundo do JavaScript, bora mergulhar nesse tópico e aprender o que são closures, para que servem e como podemos tirar proveito deles em nossos projetos.
Afinal, o que são?
São funções canibais que se alimentam de outras 🤪 ... Brincadeiras a parte, vamos a definição técnica disponível no MDN:
Uma closure é a combinação de uma função com as referências ao estado que a circunda (o ambiente léxico). Em outras palavras, uma closure lhe dá acesso ao escopo de uma função externa a partir de uma função interna - MDN Web Docs.
Então uma closure é uma função que tem acesso ao escopo de uma função externa, mesmo depois que a função externa tenha sido executada e encerrada. Isso significa que a closure pode "lembrar" e acessar variáveis e argumentos de sua função externa, mesmo que essa função já tenha sido finalizada. Legal, né?
Resumindo, nada mais é que funções que se apropriam do contexto de outras funções, no caso, funções definidas dentro do escopo de uma outra função e por isso possui acesso a variáveis e argumentos da função pai, mesmo que essa função já tenha sido finalizada. (diz aí se não são canibais 😶)
Vamos a um exemplo:
function createAdder(X) {
return function(Y) {
return X + Y;
}
}
const addFive = createAdder(5);
console.log(addFive(10)); // Output: 15
Olhando esse código pela primeira vez pode parecer confuso, afinal de contas, o que uma função está fazendo dentro da outra? E gente, pra quê raios eu preciso retornar a função que eu declarei lá dentro? 🤯 ... Calma pessoal, prometo que tentarei explicar da melhor forma possível, bora lá.
Neste exemplo, createAdder
é uma função que recebe um número X
como argumento e retorna uma nova função que soma X
com um segundo argumento Y
. Então, criamos uma nova função addFive
usando createAdder(5)
. Essa nova função recebe um número Y
como argumento e retorna a soma de X + Y
. Quando chamamos addFive(10)
, a função interna usa a referência a X
mantida em sua closure para somar X = 5
com Y = 10
, retornando 15.
Observe que, mesmo que a função createAdder
tenha concluído sua execução, a função interna retornada ainda tem acesso à variável X
definida no escopo da função externa. Isso ocorre porque a função interna é uma closure que "lembra" o ambiente em que foi criada, incluindo todas as variáveis e funções disponíveis naquele ambiente.
Tá, mas pra que isso serve? 😐
Além das utilidades que já mencionamos, closures podem ajudar a deixar seu código mais modular e organizado. Elas permitem que você crie funções com comportamentos específicos sem ter que duplicar código, e também ajudam a manter a privacidade de variáveis que não devem ser acessadas diretamente.
Por exemplo, vamos supor que você esteja criando um aplicativo de login com JavaScript e queira criar uma função que verifique se o nome de usuário e a senha estão corretos. Você pode criar uma função de verificação interna que mantém referências às credenciais do usuário como uma closure, impedindo que outras partes do código acessem essas credenciais diretamente:
function createLoginChecker(username, password) {
return function() {
return username === "admin" && password === "1234";
}
}
const checkLogin = createLoginChecker("admin", "1234")
console.log(checkLogin()); // Output: true
Observe que a função createLoginChecker
retorna uma função interna que verifica se o nome de usuário e a senha correspondem às credenciais armazenadas em suas variáveis. A função interna é uma closure que "lembra" as credenciais do usuário e as verifica toda vez que a função é chamada. Isso permite que você crie funções privadas que não podem ser acessadas por outras partes do código.
Outra vantagem das closures é que elas podem ser usadas para criar funções que retêm estados. Por exemplo, suponha que você esteja criando um aplicativo que deve rastrear quantas vezes um usuário clicou em um botão. Você pode criar uma função que retém o estado do contador interno como uma closure:
function createClickCounter() {
let count = 0;
return function() {
count++;
console.log(`Button clicked ${count} times`);
}
}
const clickHandler = createClickCounter();
// Chamando a função 3 vezes
clickHandler(); // Output: Button clicked 1 times
clickHandler(); // Output: Button clicked 2 times
clickHandler(); // Output: Button clicked 3 times
Neste exemplo, a função createClickCounter
retorna uma função interna que mantém o estado do contador count
. Cada vez que a função interna é chamada, ela incrementa o contador e registra uma mensagem no console. A closure permite que a função interna mantenha o estado do contador entre as chamadas, permitindo que você rastreie quantas vezes o botão foi clicado.
Ok, mas onde posso ver isso sendo utilizado mundo a fora?
Legal até agora apresentamos exemplos ficticios, mas que tal olhar algum framework famoso que utiliza de closures? ... Aleatoriamente peguei um exemplo do nosso querido ReactJs de uma das funções mais populares useState
.
export function useState<S>(
initialState: (() => S) | S,
): [S, Dispatch<BasicStateAction<S>>] {
const dispatcher = resolveDispatcher();
return dispatcher.useState(initialState);
}
Font - ReactJs
OK, mas useState não é uma Closure 🤨
A função useState
não é considerada um closure por si só, isso é um fato, porém ela pode retornar uma closure como resultado da chamada da função useState
retornada pelo dispatcher
. (te peguei nessa heimmm 😏)
Ela é usada para criar um estado interno em componentes React. A implementação da função usa um closure para armazenar o valor atual do estado, bem como uma função para atualizá-lo. A função retornada por useState
é uma closure que pode ser usada para atualizar o valor do estado interno.
Veja, ao chamar a função useState
do dispatcher
, que é retornada pela função useState
do módulo React, uma closure é criada e ela captura o valor atual do estado e a função para atualizar o estado. Essa closure é retornada pelo useState
do módulo React como o primeiro elemento do array retornado. A closure é uma função que tem acesso à variável state
e à função setState
definida dentro do escopo da função useState
.
Bora para o exemplo:
function Counter() {
const [count, setCount] = useState(0);
function handleClick() {
setCount(count + 1);
}
return (
<div>
<p>You clicked {count} times</p>
<button onClick={handleClick}>
Click me
</button>
</div>
);
}
Observe que a função useState
é chamada com o valor inicial de 0, e retorna uma closure que captura o valor atual de count e a função setCount
. A closure é atribuída a count
e setCount
, permitindo que a função handleClick
atualize o valor de count usando a função de atualização retornada pela closure.
Dessa forma, a função useState
retorna uma closure que permite que o estado seja atualizado através da função de atualização capturada pela closure. A closure é criada dentro da função useState
, e captura o valor atual do estado e a função para atualizá-lo, permitindo que a closure tenha acesso ao escopo em que foi criada e possa atualizar o estado de forma segura.
Finalmente, bora pro resumo
Na prática o que uma Closure faz é criar uma associação de informações com uma função que trabalha essas informações.
Apesar de estar ligada com programação funcional, isso tem também a ver com paradigma de orientação a objeto, onde as informações e métodos trabalham juntos, com isso você consegue explorar o escopo e o contexto das funções, isso deixa ainda mais claro como aplicar alguns design patterns como Strategy e a Inversão de dependência, além de criação de eventos e callbacks.
E aí, curtiu aprender sobre closures? Espero que sim! Agora que você sabe o que são closures, para que servem e como utilizá-las, é hora de colocar esse conhecimento em prática nos seus projetos. Boa sorte e até a próxima. 🤗
Posted on April 12, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.