As múltiplas personalidades do this em JavaScript

Se você já escreveu algum código JavaScript, não deve mais achar estranho quando alguém atribui uma função a uma variável. Mas o que exatamente acontece quando você faz isso? Em JavaScript, as funções podem ser passadas como parâmetros para outras funções, retornadas como valor e, como já dito, referenciadas por variáveis. A linguagem trata as funções como cidadãs de primeira classe.

O JavaScript representa funções como objetos. Quando atribuímos uma função a uma variável, estamos criando uma referência para um objeto do tipo function. Podemos ver isso de forma clara utilizando o operador typeof. O exemplo abaixo cria uma função e a atribui a variável chamada apresentar, que recebe como parâmetro um objeto com os atributos nome e email. Vamos invocar a função e a seguir usar o operador typeof para inspecionar o tipo que é referenciado pela variável usada para guardar a função:

var apresentar = function(pessoa) {
     console.log("Eu sou "+ pessoa.nome +" e meu email é: "+ pessoa.email);
}
 
apresentar({nome: "Ricardo", email: "ricardo.valeriano@caelum.com.br"});
// Eu sou Ricardo e meu email é: ricardo.valeriano@caelum.com.br
 
typeof(apresentar); // "function"

Quando invocamos uma função com o operador (), estamos utilizando uma forma de invocação chamada por alguns de invocação simples. A maneira como uma função é invocada tem efeito direto sobre como a palavra chave this é “atribuída” no corpo da função.

Quando seu código JavaScript é executado em um navegador, uma variável global chamada window é criada para representar a própria janela do navegador. E quando a forma simples de invocação é usada, o objeto referenciado por this é equivalente ao window, veja:

typeof(window); // "object"

console.log(window); // DOWWindow

var verificandoWindow = function() {
    console.log(this); 
    console.log(this === window);
}
verificandoWindow();
// DOWWindow, true

Notou que o tipo referenciado por window é object? Podemos criar nossa própria instância de object usando a chamada notação literal, que é a que foi usada para criar o parametro passado na invocação da função apresentar no nosso primeiro exemplo.

A variável window é uma referência para um objeto e podemos adicionar atributos a ele! Quando criamos uma variável fora de uma função (ou seja no escopo

  • global
  • ), automaticamente um atributo com o mesmo nome da variável é criado em window para referenciar o objeto atribuído a nova variável. Quando tentamos acessar o valor dessas variáveis, estamos, na verdade, recuperando o valor de um atributo de window. Veja o exemplo a seguir:

    var todosPodemMeVer = "porque sou um atributo de window";
    console.log(window.todosPodemMeVer);
    // porque sou um atributo de window
    
    var pessoa = {
       nome:  "Ricardo",
       email: "ricardo.valeriano@caelum.com.br" 
    }
     
    console.log(pessoa.nome);
    // Ricardo
    
    console.log(window.pessoa === pessoa);
    // true
    

    Toda função tem acesso à palavra chave this. Vamos usar isso para obter informações sobre esse objeto. Vamos criar as propriedades nome e email em window e alterar a função para buscar os valores usando atributos presentes em this, e não mais do objeto recebido como argumento:

    var nome = "João da Silva";
    window.email = "joao@da.silva";
     
    var apresentar = function() {
        console.log(this === window);
        console.log("Eu sou "+ this.nome +" e meu email é "+ this.email);
    }
    
    apresentar();
    // true
    // Eu sou João da Silva e meu email é joao@da.silva
    

    Veja que criamos um atributo novo chamado email para o objeto referenciado por window. Um atributo pode referenciar qualquer objeto, inclusive funções. Para ilustrar, vamos criar um novo atributo chamado quemSouEu em pessoa, que é uma referência para a função apresentar do exemplo anterior. Dessa forma, será possível invocar essa função diretamente a partir do objeto pessoa.

    var pessoa = {
       nome:  "Ricardo",
       email: "ricardo.valeriano@caelum.com.br" 
    }
    

    Mas a função usa internamente a variável this para fazer seu trabalho, certo? Será que isso vai continuar funcionando? Repare a saída:

    pessoa.quemSouEu = apresentar;
    pessoa.quemSouEu();
    // false
    // Eu sou Ricardo e meu email é ricardo.valeriano@caelum.com.br
     
    apresentar();// Agora novamente invocando a função com o operador ()
    // true
    // Eu sou João da Silva e meu email é joao@da.silva
    

    Uma função referenciada por um atributo de um objeto pode ser invocada como um método daquele objeto, a palavra chave this é atribuída ao objeto onde a função foi invocada.

    Uma outra forma de executar uma função no contexto de um objeto específico é invocando o método call disponível em todo objeto do tipo function. Esse método possibilita definir o objeto referenciado por this no momento da invocação:

    apresentar.call(pessoa);
    // false
    // Eu sou Ricardo e meu email é ricardo.valeriano@caelum.com.br
    

    Mas não pense que acabou! Existem ainda outras formas de invocar funções que interferem em como a palavra this é atribuída. Por exemplo, o operador new pode ser utilizado em associação com o operador () para invocar qualquer função, isso vai fazer com que o this seja atribuído a um novo objeto. O retorno da função também pode ser alterado por essa forma de invocação. Você lerá sobre esses detalhes na segunda parte desse artigo.

    9 Comentários

    1. Davi 07/05/2012 at 13:16 #

      Muito legal a postagem, recentemente escrevi também um post questionando sobre o Javascript ser o padrão da web e superar até mesmo linguagens como PHP com Node.js etc.
      http://shar.es/2vFUb

    2. wedenson barros freitas 07/05/2012 at 13:16 #

      Excelente post muito obrigado.

      Sozinho se chega mais rápido mais juntos vamos mais longe.

    3. Flavio Sakamura 07/05/2012 at 13:19 #

      Parabens!!!!! Nenhuma linguagem é facil, kkkk.

    4. Caio Ribeiro Pereira 07/05/2012 at 14:05 #

      Até animando o pessoal que curte Node.js, segue meu novo post apresentando 3 formas de trabalhar com Node.js é bem bacana e vale a pena conferir, principalmente para quem trabalha com Ruby, Python, Java, C#

      http://www.udgwebdev.com/3-maneiras-de-trabalhar-com-node-js/

    5. Vinicius Maia 07/05/2012 at 22:23 #

      Muito bom!!!

      Parabéns

    6. Ricardo Valeriano 11/05/2012 at 15:27 #

      Muito obrigado senhores. Fiquem ligados que em breve teremos mais detalhes sobre essas e outras características da linguagem! =)

    Deixe uma resposta