"Todos os seres humanos têm três vidas: a pública, a privada, e a secreta." Gabriel García Márquez

Para começarmos faça o proposto a seguir e prossiga:
Ex1: Crie uma função que recebe um parâmetro "x" e retorna uma função sem parâmetros que quando invocada retorna "2x".

Esse é um dos exercícios propostos por Douglas Crockford e a resposta está abaixo:

var globalvar;  
function externa(x){  
    function interna(){
        return 2*x;
    }
    return interna;
}

var dobra3 = externa(3);  
console.log(dobra3()); //6  

Esse exercício é interessante, pois possui a estrutura necessária para definirmos closures:

Closure é dar continuidade à vida de variáveis locais de uma função, após essa função ter sido invocada.

Em nosso caso, a função interna enclausura a variável local x da função externa. Assim, mesmo quando a função externa é invocada, a variável x permanece viva dentro da função interna.

É como se a função interna falasse: "x", você não está no meu escopo, mas eu dependo de você pra funcionar corretamente. Então, não se preocupe, eu vou lhe deixar vivo até que eu cumpra minha missão na Terra. E para a função externa: "Não se preocupe, o legado que você deixou comigo está bem guardado e ninguém vai vê-lo a não ser eu."

Neste ponto,já podemos mostrar por que closures são tão importantes, através de suas aplicações:

  • Alteração de escopo;
  • Encapsulamento de variáveis;
  • Fabricar funções ou especializá-las;
  • Criação de módulos;

Quem não reconhece a importância disso,acaba não reconhecendo a importância do Javascript e todos seus frameworks. É, por causa disso, que esse é um conceito tão fundamental e deve ser dominado.

Vamos prosseguir, abordando cada uma de suas aplicações.

Alteração de Escopo

Imagine que você queira saber a quantidade de vezes que um botão foi clicado, como você faria isso? Vamos fazer melhor, digamos que você esteja fazendo um site de cursos online como o Codeschool ou o Codecademy em que existe um exercício e um botão de dicas, sendo que você pode pedir no máximo 3 dicas, como você implementaria isso?

Uma das soluções seria criar uma variável global e incrementá-la todas as vezes que o evento click acontecer:

//Contador global
var counter = 0;  
var element = document.getElementById('button');

element.onclick = function() {  
    // Incrementa contador toda vez que o elemento é clicado
    counter++;

    if (counter === 1) {
        console.log("Estude closures!");
    }
    elseif (counter === 2) {
        console.log("Estude prototypes!");  
    }
    elseif (counter === 3) {
        console.log("Estude padrões de projeto!");  
    }
    else{
        console.log("Continue lendo o Programa Objetivo!"); 
    }
};

Mas uma solução muito melhor é usar o conceito de closure, pois evita possíveis transtornos decorrentes do uso de variáveis globais:

E aí, muito legal, não é mesmo? Nada melhor que um exemplo prático pra demonstrar que um conceito teórico não está tão distante da realidade.

Encapsulamento

Encapsulamento também conhecido como o ato de "esconder informação" é um conceito fundamental em todas as linguagens orientadas a objeto. Mas javascript não é uma linguagem propriamente orientada a objetos é?

Não, Javascript é um híbrido de linguagem funcional e orientação a objetos. Javascript é uma espécie de mestiço oscilando entre paradigmas, então, talvez por isso que private accessors não existam na linguagem.

No entanto, não confunda:
"Javascript tem mecanismos de encapsulamento. O que ele não possui são os chamados private accessors".

Digamos que você tem um dinheiro no cofre de casa e você não quer que ninguém saiba quanto existe nele, mas, ao mesmo tempo, quer que as pessoas possam depositar e sacar dinheiro. O que você faz?

function createVault(yourSecretMoney){  
  var myVault = {}; //seu cofre
  var mySecretMoney = yourSecretMoney; //seu dinheiro secreto

  function sacar(x){
    if(!mySecretMoney){
      console.log("O cofre não tem mais dinheiro");
      return 0;
    }
    if(mySecretMoney - x < 0){
      console.log("Não temos a quantia solicitada.");
      return 0;
    }

    mySecretMoney = mySecretMoney - x;
    console.log("Você sacou R$" + x);
    return x; 
  }

  function depositar(x){
    mySecretMoney = mySecretMoney + x;
    console.log("Obrigado!");
  }

  myVault.sacar = sacar;
  myVault.depositar = depositar;

  return myVault;
} 

var pig = createVault(10);  
pig.sacar(2); //Você sacou R$2  
pig.sacar(9);  //Não temos a quantia solicitada  
pig.depositar(3); // Obrigado!  
pig.sacar(9);  // Você sacou R$9  

Fabricando Funções

Imagine que você quer fazer um jogo para que crianças aprendam tabuada, o jogo funciona da seguinte forma:

  1. O usuário escolhe a operação da tabuada.
  2. O usuário escolhe com que número deseja praticar.
  3. Ele clica em um botão para gerar o resultado da operação selecionada, utilizando um fator de 1 a 9 escolhido aleatoriamente.
  4. Ele digita a resposta e programa diz se ele acertou.

Para implementação do passo 1, escolhi criar uma função chamada "generateOperation" que vai criar um fator aleatório a ser utilizado na operação escolhida pelo usuário. A utilização da fábrica de funções se deve justamente a isso: para que não tenhamos que repetir a chamada do fator aleatório em todas as operações possíveis.

A implementação de 1 a 3 segue abaixo, mas você pode ver diretamente aqui, onde a indentação está melhor.

Recomendo, porém, que você refaça esses passos para verificar que há uma certa dificuldade em fazer com que os eventos do botão não se acumulem. E, depois disso, implemente o passo 4. Se quiser, me envie a solução e eu colocarei aqui no post ou simplesmente coloque seu link do JSFiddle nos comentários.

Criação de Módulos

Que tecnologia tende a ser mais proeminente nos próximos 5 anos? O próprio fundador do Quora com passagens pela Amazon e Facebook, Charlie Cheever, acredita é Node.js, posto que positivou a resposta no link acima.

E que outra invenção deu suporte à enorme popularização do Node?
O NPM, que nada mais é que um gerenciador magnífico de módulos. Acho que a essa altura já deu pra dar uma idéia da importância de closures dentro dessa linguagem. Vamos a um exemplo simples de criação de módulos para você pegar o "feeling" do négocio. Falo isso, porque módulo e module pattern são tão importantes que merecem um artigo tratando somente deles. Vejamos:

var MYMATHMODULE = (function(module){  
  var pi = 3.14;
  module.PI = pi;

  module.CircleArea = function (radius) {
    return pi * radius * radius;
  }

  //várias outras funções matemáticas
  //...

  return module;
})(MYMATHMODULE || {});

console.log(MYMATHMODULE.PI); // 3.14  
console.log(MYMATHMODULE.CircleArea(2))  //12.56  

Próximos passos

Você vai ver que funções em Javascript são ainda mais poderosas do que você pensava:

  • O Poder das Javascript Functions

Sobre o Autor

Johel Carvalho

Johel Carvalho

Engenheiro Civil formado pelo Instituto Militar de Engenharia (IME-RJ) em Dezembro de 2012. Largou a profissão pelo desenvolvimento web, começando com C# e sendo atualmente aficionado por JavaScript.

comments powered by Disqus