Closure e o tal do Hoisting
Closure ? Hoisting ? Instanciação usando uma IIFE? WTF ?
Hoisting
Não precisa ser um gênio para entender. Muitos que não sabem seu significado pode está pensando “Nossa, deve ser algo difícil!”, mas na verdade precisa mais de atenção, a linguagem usa e você não tá percebendo, porém pode causar bastante confusão se não souber da sua existência, por ter comportamentos ‘ocultos’. Então o que significa Hoisting ? Traduzindo para português, seria ‘elevação’, e isso tem todo sentido no JavaScript. Antes de mostrar um exemplo com hoisting, é importante entender como funciona a declaração de uma variável no javascript.
Exemplo 1 A palavra reservada var especifica que a variável seja do escopo atual, com isso, usando a variável nome em outras funcoes não irá atrapalhar, pois ela apenas faz parte do seu escopo.
Exemplo 2 Repare que damos um nome de calango, mas a funcao modificaNome( ) altera o nome da variável, isso acontece por ela ser uma variável global, um conceito importante que ainda veremos melhor.
O erro acontece pois não declaramos nenhum cachorro, então vamos lá =)
Retorna underfine por ter uma variável no seu escopo imprimeCachorro( ) declarada, porém não instanciada. Você pode tá se perguntando ‘Como não foi instanciada se antes da função eu defino o cachorro recebendo dog e dentro da função defino cachorro recebendo spike ?’
A lógica de muitas linguagens seria mostrar ‘dog’, pois já foi instanciada no topo. Taí um dos porquês que você precisa estudar a linguagem como funciona e não apenas sua sintaxe, JavaScript diferente de algumas linguagens, tem seus comportamentos diferenciado, e se você já estudou C, vai perceber que a sua ação é diferente. E é aí amiguinho que a mágica acontece, a variávei foi ‘hoisteada’, elevada para o topo. Isso acontece pois o compilador na verdade, declara todas as variáveis, logo que seu código começa a ser compilado.
Como acontece? ⬇
Exemplo 5 Logo depois ele executará a funcao imprimeCachorro( ), e entao ele pula as declarações e começa a executar o console.log(cachorro), porém não houve nenhuma atribuição ao cachorro dentro do imprimeCachorro( ), já que foi declarada e hoisteada, e com isso ele continuou com o valor underfine e imprimiu.
Com isso, já sabemos o que significa o underfine quando aparecer.
A mesma coisa pode ser aplicada para uma função.
Exemplo 6 Acontece que as duas funções pegaValor( ) foram jogadas para o topo, e em seguida é retornada a última pegaValor( ), pois ela sobrescreve a primeira.
Como acontece ? ⬇
E agora ? Vai ser o mesmo resultado ?
Agora cuidado, estamos usando uma funções anônima, com isso a declaração sobe e as funções (atribuição) continuam no mesmo lugar, e assim prevalecendo sempre a de cima.
Como acontece ? ⬇
Closures
Traduzindo closure, podemos dizer que é algo de encerramento, no sentido de guardar, pôr em um lugar fechado. JavaScript não é a única a usar essa poderosa técnica, ela veio das linguagens funcionais, mas que acabou difundindo e implementado em outras linguagens como C#.
Um exemplo comum ⬇
Mas cuidado, o somador não é uma closure, e sim o seu valor que retorna.
Outro exemplo ಠ_ಠ
Exemplo 10 Note que ao chamar a tela, ele usa a variável mensagem, que está no escopo mostrarMensagem( ), a função de dentro (inner), vai ter o acesso as variaveis da funcao de fora (outer).
Mais outro exemplo, e se reclamar vem mais outro ლ(ಠ▃ಠლ)
Na prática ⬇
Primeiro vamos imaginar que precisamos saber quantas vezes o usuário está clicando no botão. Se você entendeu mesmo, não vai querer usar uma variável global pra sair contando, isso só vai te trazer prejuízo na frente.
"Todos os seres humanos têm três vidas: a pública, a privada, e a secreta."
Gabriel García Márquez
Não podemos ficar criando variáveis globais, pois elas ficam livre ao longo do código e provavelmente pode-se modificar e trazer dor de cabeça. Através de closure, podemos deixar ela escondida, ou seja, privada. Precisamos usar encapsulamento, um conceito das linguagens orientada a objetos, ora mas JavaScript é orientada a objetos ? NÃO. Porém, todavia, JS é linda, podemos criar um mecanismo de privar uma variável usando closure, e se você prestar bem atenção, não vai ter nenhuma dúvida. Vamos criar um exemplo clássico, um banco onde posso depositar e sacar, claro que quero meu dinheiro privado.
Boas práticas: (⌒‿⌒)
Em projetos grande um dos problemas que acontecem muito é o de comportamento, alguns por está conseguindo acessar variáveis que não deviam, vazando escopo, sempre feche o escopo, se organize e não fique criando variáveis globais.
Variáveis Globais
Falamos bastante sobre não criar variáveis globais, e que isso pode lhe trazer dores de cabeça lá na frente, mas por quê ? Elas podem ser acessadas por qualquer escopo, e possibilitando as chances de você modificar ou usar o mesmo nome.
1.
2.
Exemplo 14/15 Na exemplo 2 a função alteraMensagem, não foi criado nenhuma variável, apenas atribuição, por isso ela modificou, diferente do exemplo 1, onde tem uma palavra reservada var, onde indica uma declaração no escopo atual.
Variáveis por parâmetro
É comum demais passarmos parâmetros pela nossas funções, porém no JavaScript não precisamos especificar que tipo de dados vamos receber.
Um das coisas curiosas é que ele não verifica nossos argumentos para dar erro de sintaxe. Como por exemplo:⬇
Mas por quê ?
Simples, como não foi passado por parâmetro, ela é setada como underfined, e você não pode querer somar um número com underfined.
E quando existe mais parâmetros do que o esperado ?
Sem problemas, vamos ver outro exemplo ٩(͡๏̯͡๏)۶
Preciso nem explicar o que houve. (ړײ
Instanciação usando uma IIFE
IIFE é comumente chamado de “fechamento” ou envoltório.
Por que usar isso ?
Variáveis globais é um enorme risco, onde pode ocorrer colisões de nomes e trazendo grandes consequências e possívelmente um dos problemas mais difícieis de detectar. Com JavaScript é apenas uma questão de adequação, você declara dentro de um escopo local para que ela não fique jogada no escopo global, e claro, nunca esquecer da palavra-chave var, pois sem ela, a variável é global.
Mas por quê ?
Simples, como não foi passado por parâmetro, ela é setada como underfined, e você não pode querer somar um número com underfined.
Vamos ver outro exemplo ٩(͡๏̯͡๏)۶
Cuidado ao pensar que seja 4, ele chama logo preservando o contexto e retornando o argumento.
Nesse exemplo sim ⬇ ele é chamado apenas depois da atribuição no n.
Conclusão
Dessa forma aprendemos vários conceitos, todos eles são importantes, e alguns possuem até livros específicos como o de Closure: The Definitive Guide, então nunca descarte de sempre estudar.