@rodbv

Javascript pra gente grande: entendendo closures

com 16 comentários

Crock NorrisNo fim-de-semana passado estive na QCon, evento muito legal que contou com a presença do “guru” do Javascript, Douglas Crockford.

Ele deu uma geral no passado e futuro da linguagem Javascript Ecmascript, e um dos slides que mais me chamou dizia o seguinte (tradução minha):

Escrever código ECMAScript sem entender closures é como escrever código Java sem entender classes.

Mensagem mais clara, impossível. E eu concordo. Javascript é uma linguagem que quase ninguém realmente senta pra estudar: a maioria das pessoas simplesmente assume que ela é meio parecida com Java (Java+script, certo?), e assim vai, escrevendo funções e declarando variáveis.

Mas para quem realmente se dedica a estudar Javascript um pouco mais a fundo, descobre uma bela linguagem, altamente funcional (no sentido F# da coisa) e expressiva.

Pra podermos tirar vantagem dessa linguagem, é importante entendermos o tripé conceitual que forma o Javascript:

  • Funções
  • Objetos
  • Closures

Vamos então dar uma olhada nesses três elementos:

Funções

No Javascript, funções são cidadãos de primeira classe. Isso significa que você pode assinalar uma função a uma variável, passar uma função como parâmetro em outra função, ou retornar uma função a partir de outra função, sem complicações sintáticas.

//funções como variáveis
var fn = function(mensagem){
    alert(mensagem);
};
fn("Ola"); //"Olá"

//função como parâmetro
function chamaFuncao(funcao, texto){
    funcao(texto);
}
chamaFuncao(alert, "Ola de novo"); //Olá de novo

//funcao sendo retornada em outra funcao (gerador de funcoes)
function geraSomador(numero){
    return function(x) { return x + numero;};
}
var soma5 = geraSomador(5);
alert(soma5(4)); //"9"

Objetos

Objetos em Javascript são simplesmente uma coleção de chaves e valores. Tudo em Javascript são objetos: funções, arrays, expressões regulares etc.

O fato de objetos serem apenas uma coleção de chaves e valores fazem que a sintaxe de criação seja bem simples:

var pessoa = {
     nome : "Douglas Crockford",
     profissao : "Guru Javascript"
};
alert(pessoa.nome); //Douglas Crockford
alert(pessoa["profissao"]); //Guru Javascript

Note também que eu posso acessar um atributo de um método tanto usando a notação com ponto, quanto usando a notação com colchete. A notação da linha 6 permite que eu acesse tais atributos dinamicamente, o que pode ser muito útil. Podemos também adicionar novos atributos a um objeto, depois de ele ter sido criado:

pessoa.email = "crock@yahoo.com";
pessoa["telefone"] = "+1 746 4840";
alert(pessoa.email); //crock@yahoo.com
alert(pessoa.telefone); //+1 746 4840
alert(pessoa.umAtributoQueNaoExiste); //undefined

Como o valor de um atributo pode ser qualquer objeto, podemos colocar funções e outros objetos ali:

var pessoa = {
    nome: "Douglas Crockford",
    dizAlgumaCoisa: function(coisa){
        alert(coisa);
    },
    telefones: {
        casa: "+1 434 3301",
        trabalho: "+1 434 9785"
    }
}

pessoa.dizAlgumaCoisa("Pode me chamar de Crock"); //Pode me chamar de Crock
alert(pessoa.telefones.casa); //+1 434 3301

Vendo o código cima já dá pra sentir um jeitão de orientação a objetos que tanto gostamos em C# e Java; mas o que temos em mãos é um objeto, como criamos algo que funciona como uma classe? Fácil, basta criarmos uma função que retorna objetos, e usarmos a palavra-chave new:

function Pessoa(nomeDaPessoa){
    return {
        nome: nomeDaPessoa,
        dizAlgumaCoisa: function(coisa){
            alert(coisa);
        },
        telefones: {
            casa: "+1 434 3301",
            trabalho: "+1 434 9785"
        };
    }
}

var crock = new Pessoa("Douglas Crockford");
crock.dizAlgumaCoisa(crock.nome);  //Douglas Crockford
crock.dizAlgumaCoisa(crock.telefones.trabalho); //+1 434 9785

Tá ficando legal, mas quando pensamos em classes, uma das primeiras coisas que vêm à cabeça é a possibilidade de termos atributos privados, e é aí que closures vêm nos ajudar.

Closures

Closure em inglês significa encerramento, no sentido de guardar, pôr em um lugar fechado. Repare no código que usamos lá em cima:

//funcao sendo retornada em outra funcao (gerador de funcoes)
function geraSomador(numero){
    return function(x) { return x + numero;};
}
var soma5 = geraSomador(5);
alert(soma5(4)); //"9"

Uma coisa muito curiosa aconteceu aqui: a minha nova função, soma5, de alguma forma lembra (encerra) o valor do parâmetro numero, depois de criada. Então tudo que declaramos dentro de uma função é privado, mas pode ser usado por membros públicos.

Resumindo então: closures são blocos de código que podem ser executados a qualquer momento, guardando o contexto no qual eles foram criados, mesmo quando a função que criou esse bloco já saiu de escopo.

..e isso obviamente vai cair muito bem pra gente criar uma classe bem encapsulada!

function Pessoa(nomeDaPessoa){
    //membros privados
    var _livros = [];
    var _ordenaLivros = function(){
        return _livros.sort();
    }

    //membros publicos
    return {
        adicionaLivro: function(livro) {
            _livros.push(livro);
            //return this;
        },
        meusLivros: function(){
            return _ordenaLivros(_livros).join(", ");
        }
    };
}

var p = new Pessoa("Crock Norris");
p.adicionaLivro("Javascript: The Good Parts");
p.adicionaLivro("Das Beste an Javascript");
alert(p.meusLivros()); //Das Beste an Javascript, Javascript: The Good Parts

Repare que o método adicionaLivro tem um return this comentado; se a gente descomentar essa linha, a função vai retornar uma referência ao próprio objeto. Isso permite encadeamento de métodos, para uma interface mais fluente:

    alert(p.adicionaLivro("Javascript: the good parts")
        .adicionaLivro("Das Beste an Javascript")
        .meusLivros()); //Das Beste an Javascript, Javascript: The Good Parts

Um outro uso legal de closures é evitar a criação desnecessária de variáveis globais (uma coisa ruim em qualquer linguagem). Podemos usar uma função anônima que é executada imediatamente (linha 8 ) para criar um bloco de código com suas variáveis locais, que saem de escopo assim que a função termina (ao invés de ficarem penduradas na página pra sempre) e evita conflitos com outras variáveis e funções da página:

//....
cor = "blue";
//...

(function(){
    var cor = "#FF0000";
    document.body.style.background = cor; //deixa o fundo da pagina vermelha
})();
alert(cor); //blue

Bem, por enquanto é isso :) Pretendo escrever mais sobre aspectos legais do Javascript, aceito sugestões.

Escrito por rodbv

16/09/2010 às 00:59

Publicado em Javascript, Programação

16 Respostas

Assinar os comentários com RSS.

  1. Parabéns pelo post. Além de mostrar bem o espírito que o Crockford quis passar na palestra, você organizou passo a passo a transição de se basear em classes para se basear em closures.

    Paulo Silveira

    16/09/2010 em 05:08

  2. Valeu! E parabéns pela organização do QCon-SP, foi sensacional.

    Rodrigo Vieira

    16/09/2010 em 15:26

  3. Muito bom o artigo, direto ao ponto e sem rodeios.

    Apenas um comentário sobre a colocação de que “quem não entende closures não merece respeito”: Não sei em que contexto isso foi dito, mas se levado ao pé da letra essa afirmação é um pouco radical ao meu ver.

    Eu por exemplo, já conhecia empiricamente as técnicas aqui mostradas para criar “classes” em JS, mas se você me perguntasse “o que são closures em JS” eu não saberia responder… por este motivo não mereceria respeito?

    Acredito que o sr. Crockford não concorde com esta interpretação literal, por isso levanto a discussão para que seus admiradores/seguidores/leitores também fiquem atentos para radicalismos verbais como este.

    Rodrigo Peplau

    16/09/2010 em 16:09

  4. Fala xará,

    A frase do Crockford foi aquela que eu escrevi lá em cima.. a parte do “respeito” foi brincadeira minha mesmo, melhor deixar claro!

    Realmente dá pra fazer muita coisa boa em JS sem saber o conceito de closures formalmente, mas a partir do momento que a gente entende isso, ajuda muito. Até porque é MUITO fácil a gente acabar com um bug estranho porque tá rolando um closure em algum lugar e a gente não sabe bem.

    Pretendo escrever mais um post sobre closures, pois ele é importante pra muito mais coisas do que permitir classes com membros privados.

    Abraços!

    Rodrigo Vieira

    16/09/2010 em 16:17

  5. Ah entendi cara, você deu uma dramatizada, hehehe…

    Me corrija se estiver errado: basicamente o que eu entendi do conceito de closure pode ser resumido em:

    - Variaveis declaradas fora de qualquer função tem escopo global
    - Variaveis declaradas dentro de uma função ficam com escopo limitado àquela função

    Isso realmente pode confundir bastante, principalmente se você usa por exmeplo o famoso “document ready” do jquery, que requer a criação de uma função.

    Rodrigo Peplau

    16/09/2010 em 16:23

  6. Muito bom o post. Muito didático, ficou bem claro closures.

    Thiago

    16/09/2010 em 16:45

  7. Isso aí Peplau.
    Fora isso, se uma função B é declarada dentro de uma função A, ela terá acesso a variáveis locais de A quando for executada, mesmo que A já tenha terminado de executar.

    rodbv

    16/09/2010 em 17:43

  8. Saquei xará… té mais!

    Rodrigo Peplau

    16/09/2010 em 18:19

  9. Parabéns, ótima matéria sobre closures. Já li sobre o assunto e achei muito direta e bem explicada com exemplos muito práticos. Parabéns. Recomendações a você.

    Gartz

    16/09/2010 em 18:50

  10. Muito bom o post.

    Parabéns pelo retorno do blog!

    Abçs.

    Zé Filipe

    José Filipe

    17/09/2010 em 01:39

  11. [...] um comentário » Ontem escrevi sobre closures com JavaScript, e o post foi muito bem recebido. A maioria dizendo que já usava [...]

  12. [...] um comentário » Ontem escrevi sobre closures com JavaScript, e o post foi muito bem recebido. A maioria dizendo que já usava [...]

  13. Confesso que sempre olhei torto para Javascript e só comecei a gostar mesmo faz pouco tempo. Puro preconceito: posts como este só reforçam que é uma linguagem muito bacana.

    Ótimo post!

    Marcel Castilho

    17/09/2010 em 18:16

  14. Ficou bom mesmo, não conhecia o conceito de closures.

    bruno taboada

    17/09/2010 em 21:17

  15. [...] vimos no post sobre closures, esse padrão de funções-dentro-de-funções nos permite criar pseudo-classes, com métodos [...]

  16. VALEUUUUU, ENTENDI A FUNÇÃO ANONIMA, NA VERDADE JA ENTENDIA O FUNCIONAMENTO DE CLOSURE, MAS AGORA ENTENDI MAIS UM POCO, VO PESQUISA MAIS 1 POCO PRA ENTENDER DE UMA VEZ ESSA COISA, MTO DIFICILL .. :/
    VLW CARA

    fernando

    09/01/2012 em 22:02


Deixe uma resposta

Preencha os seus dados abaixo ou clique em um ícone para log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Sair / Alterar )

Imagem do Twitter

You are commenting using your Twitter account. Sair / Alterar )

Foto do Facebook

You are commenting using your Facebook account. Sair / Alterar )

Connecting to %s

Seguir

Obtenha todo post novo entregue na sua caixa de entrada.