Dominando o “this” do JavaScript – 4/6

Nos últimos 3 posts, você viu as três principais formas de padrão de invocação em JavaScrips e como esses padrões manipulam o valor do “this” nas funções chamadas. Esses padrões de invocação não são as únicas formas que você pode manipular o “this”. Hoje você verá dois métodos que permitem manipular explícitamente o “this” em quase toda chamada de função no JavaScript.

 

Funções com métodos

Quase tudo no JavaScript são objetos, ou ao menos, se comportam como objetos quando métodos e atributos são acessados. Função em JavaScript não é exceção. Elas podem ser tratadas como a maioria dos objetos, na maior parte do tempo. Elas possuem métodos e atributos que podem ser cancelados ou lidos (mas alguns deles são protegidos e não podem ser modificados).

Entre os métodos que as funções possuem, existem os métodos “.call” e o “.apply”. Estes métodos existem em todas as funções e eles permitem que você invoque uma função com um contexto específico, provendo parâmetros para a função em uma de duas formas distintas.

 

Utilizando o .call

Ao utilizar o “.call” em uma função, o primeiro parâmetro será o contexto ou a variável “this”. Qualquer parâmetro adicional será enviado como argumentos à função original.


function adicionar(b){
  return this.a + b;
}
var meuContexto = {a: 1};
var resultado = adicionar.call(meuContexto, 2);
console.log(resultado); // => 3

Você pode informar qualquer quantidade de argumentos ao método “.call”, e todos eles serão passados à função original após a definição do contexto.

O método “.call” é mais utilizado quando você precisa chamar uma função dinamicamente com um determinado contexto e definir um conjunto de valores que você já possui como variáveis.

 

Utilizando o .apply

A utilização do “.apply” é essencialmente a mesma do “.call” com exceção que você passa um array como segundo parâmetro e esse array é passado para a função original como uma lista de argumentos.


function adicionar(b, c){
  return this.a + b + c;
}
var meuContexto = {a: 1};
var resultado = adicionar.apply(meuContexto, [2, 3]);
console.log(resultado); // => 6

Perceba que qualquer parâmetro que você deseja passar ao “.apply” (após o primeiro, que define o “this”) tem que estar em um array. Se você esquecer de informar parâmetros adicionais como um array, você terá um erro.
O método “.apply” é mais utilizado quando você precisa chamar uma função dinamicamente com um determinado contexto e um conjunto de valores em um array, tipo separando os argumentos de outra função.

 

Similares, mas diferentes

Utilizando “.call” ou “.apply” é algo muito parecido, mas ligeiramente diferente na forma da passagem de parâmetros à função original. Com o “.call” os parâmetros são passados como uma lista de parâmetros comuns. com o “.apply” os parâmetros são passados como um array. A ligeira diferença entre as duas formas de invocação de métodos permite muita flexibilidade no seu código. Com “.apply”, você pode dinamicamente acessar métodos e passar parâmetros sem precisar saber quantos parâmetros são necessários. Com o “.call”, você define o contexto do método e passa uma lista específica de parâmetros que você já conhece.

 

Padrões de sobrecarga de invocação

Um dos efeitos colaterais do uso do “.apply” e do “.call”, se você não tiver cuidado, é que eles podem sobrescrever o contexto que pode ter sido definido pela invocação do método ou função.
Se, por exemplo, você tiver um objeto com atributo e método:


var meuObjeto = {
  nome: "meu nome",
  fazerAlgo: function(){
               console.log(this.nome);
             }
};

e você chamar o método com “.call” ou “.apply”, você está realizando uma sobrecarga no padrão de invocação objeto-ponto-método.


meuObjeto.fazerAlgo.call({});

Neste exemplo, “fazerAlgo.call” não segue o padrão objeto-ponto-método que foi explicado no segundo post da série. Em vez disso, “.call” sobrecarrega o valor do “this” para a execução do “fazerAlgo”. E com o contexto “this” vazio passado por parâmetro na chamada acima, não existirá nenhum atributo “nome” e o resultado do “console.log” será “undefined”.

 

Onde usar

A utilização do “.call” e “.apply” se torna importante quando utilizamos objetos e métodos callback. Por exemplo:


var meuObjeto = {
  nome: "Meu Nome",
  algumaCoisa: function(cb){
                 cb();
               },
  fazerAlgo: function(){
               console.log(this.nome);
             }
};

meuObjeto.algumaCoisa(meuObjeto.fazerAlgo);


Neste exemplo (que por sinal é muito simples somente para ilustrar o caso) a chamada para “meuObjeto.algumaCoisa” recebe “meuObjeto.fazerAlgo” como parâmetro. Desde que o parâmetro não inclua o “()” (que invocaria a função antes de executar o “algumaCoisa”) para que a função seja passada como um ponteiro para função, se tornando efetivamente um método callback. O método callback é então invocado no padrão de chamadas diretas, ou seja, o valor do “this” recai no contexto que já vimos, ou será o contexto global ou indefinido, dependendo do ambiente de execução (como foi mencionado no post 1).

Uma vez que a chamada de “cb()” aponta para o método “fazerAlgo”, o valor do “this” não será “meuObjeto” e por decorrência não contém o atributo “nome”. Então, o resultado exibido pelo “console.log” será algum valor inexistente ou mesmo undefined.

As ramificações disso são significativas. Esta combinação de diversos padrões podem levá-lo a diversos caminhos errados ao analisar qual o real valor do “this”. Entretando, é o padrão de chamada direta do “cb()” que determina o valor do “this” e não qualquer outro padrão de chamada abordados aqui.

 

Muita coisa pra digerir

Eu sei que é muita coisa pra digerir. Você viu anteriormente os principais padrões de invocação de funções. Agora você viu dois métodos adicionais para modificação e sobrecarga do valor do “this” e como podem ser utilizados em conjunto com os padrões aneriores. Realmente isso pode ser maçante no início. Mas não se preocupe se você ficar meio perdido, é normal. Dê algum tempo para compreender. O melhor a fazer é fazer experimentos no código e ver o que acontece.

 

Cenas do próximo capítulo

Você sabia que existe uma última dica neste exemplo? No que diz respeito ao padrão de invocação de chamada direta do “cb()” é possível garantir o valor do “this” dentro da função “fazerAlgo”, utilizando o “.bind”. Mas isso fica para o próximo post desta série Dominando o “this” do JavaScript.
Se houver alguma dúvida, sugestão ou percebeu algum erro de digitação, não hesite em entrar em contato que responderei tão rápido quanto puder e atribuirei o crédito pela ajuda.

Até mais!
André Fellows

Leave a Reply Cancel reply