"Toda reforma interior e toda mudança para melhor dependem exclusivamente da aplicação do nosso próprio esforço." - Immanuel Kant
Anteriormente, vimos Como Dominar Javascript "this" com 3 exemplos. Agora iremos ver 3 maneiras de mudar seu valor, conforme nossa necessidade.
A habilidade de usar e manipular o valor que this assume em funções é essencial para boa programação orientada a objetos em JavaScript. Funções podem ser utilizadas em diferentes contextos e temos que ser capazes de trabalhar com elas em cada situação.
Normalmente this é automaticamente atribuído, mas você pode alterar seu valor por várias razões(reaproveitamento de código, por exemplo), conforme veremos nos exemplos a seguir.
Existem 3 métodos de função capazes de alterar o valor this: Call, Apply e Bind. Por método de função queremos dizer que toda função em JavaScript possui nativamente implementada essas 3 propriedades. O mecanismo de herança que permite isso acontecer será visto muito em breve quando apresentarmos os prototypes.
Call
Este é o primeiro método de funções capaz de alterar o valor this. O primeiro parâmetro que recebe é o valor de this que que será atribuído à função. Os demais parâmetros são os parâmetros da função que invoca o método Call. Vejamos:
function sayThis(n1,n2){
console.log(this * n1 * n2);
}
// 2 é this, n1 e n2 são 3
sayThis.call(2, 3, 3) // 18
// 1 é this, n1 e n2 são 3
sayThis.call(1, 3, 3) // 9
A lógica do funcionamento está clara acima, mas o legal mesmo é utilizá-la para reaproveitamento de código. Vejamos outro exemplo bem simples que dará essa idéia:
function sayAnyCharacter() {
console.log(this.name + ": " + this.character);
};
var actor1 = {
name:"Clint Eastwood",
character: "The Good"
};
var actor2 = {
name:"Lee Van Cleef",
character: "The Bad"
};
// Clint Eastwood: The Good
sayAnyCharacter.call(actor1);
// Lee Van Cleef: The Bad
sayAnyCharacter.call(actor2);
Estrutura de dados são dados sem comportamento.
Função é comportamento sem dados.
Objetos são dados com comportamento(métodos).
O exemplo acima mostra uma capacidade interessante do JavaScript, ele é capaz de separar totalmente os dados do comportamento e ainda assim facilmente torná-los simultaneamente úteis pelo poder de combiná-los. Imagine que você tivesse um arquivo de dados JSON. A demonstração acima mostra que seria facilmente possível, através do método call, transformar dados em objetos ao conferir aos primeiros um comportamento desejado. Essa capacidade é uma das coisas que torna JavaScript tão interessante.
Reaproveitando métodos nativos
Em nosso artigo Saiba Tudo Sobre Callbacks, nos deparamos com a seguinte situação: filtrar elementos input DOM que possuíam determinada característica.
Lá explicamos que os inputs retornados pelo seguinte trecho de código var inputs = document.getElementsByTagName("input");
possuem a mesma estrutura de arrays, mas não possuem o método filter no qual estamos realmente interessados.
Sabemos que se inputs fosse realmente array tudo estaria resolvido. Mas existe uma segunda alternativa. Podemos aproveitar a estrutura do filter dos arrays, modificando apenas o objeto(this) que sofrerá a ação do método. E foi exatamente o que fizemos ao criar a função filterDomElements:
function filterDomElements(elements, filterCallback) {
return Array.prototype.filter.call(elements, filterCallback);
}
Ou ainda:
function filterDomElements(elements, filterCallback) {
return [].filter.call(elements, filterCallback);
}
Apply
Apply é o segundo método de funções capaz de alterar o valor this. Ele funciona exatamente como o método Call, porém seu segundo parâmetro recebe um Array ou Array-like dos parâmetros da função. Vejamos o mesmo primeiro exemplo apresentado aqui, mas agora utilizando o apply:
function sayThis(n1,n2){
console.log(this * n1 * n2);
}
// 2 é this, n1 e n2 são 3
sayThis.apply(2, [3, 3]) // 18
// 1 é this, n1 e n2 são 3
sayThis.apply(1, [3, 3]) // 9
O método apply é especialmente útil para se trabalhar com o Arguments Object (ver O poder das Javascript Functions).
Douglas Crockford em uma de suas palestras propôs o seguinte exercício: "Escreva uma função que recebe uma outra função como parâmetro e a retorna de forma que ela só possa ser invocada uma única vez".
Segue a implementação do exercício, como você o resolveria usando Call? Ver no JSFiddle.
Bind
O terceiro método de função com a capacidade de mudar o this foi adicionado no ECMAScript5 e difere bastante dos outros dois.
Ao contrário dos outros, ele não executa a função, mas retorna uma outra. O primeiro argumento recebe o valor do this a ser usado na função a ser retornada. Os demais argumentos são os parâmetros que terão valores permanentemente atribuídos dentro da função a ser retornada.
Assim o método Bind acaba cumprindo dois papéis:
- Mudar o valor do this;
- Alterar o número de parâmetros de uma função, caso o programador deseje. Essa capacidade de alterar a quantidade de argumentos de uma função é chamada de Currying e será vista com mais detalhes posteriormente.
Dito isso, vamos ao nosso primeiro exemplo, agora usando Bind:
function sayThis(n1,n2){
console.log(this * n1 * n2);
}
var thisIs2 = sayThis.bind(2);
var thisIs2N1Is3 = sayThis.bind(2,3);
var thisIs2N1Is3N2Is3 = sayThis.bind(2,3,3);
thisIs2(3,3); //18 - muda this
thisIs2N1Is3(3); //18 - muda this e atribui n1
thisIs2N1Is3N2Is3(); //18 - muda this e atribui n1 e n2
Um bom exemplo de quando o método Bind se torna especialmente útil é quando você precisa manipular valores dentro de callbacks. O exemplo a seguir, também utilizado no post "Como Dominar This", mostra bem isso:
Nota de Conclusão
Call, Apply e Bind são apenas métodos e são tão claros como água cristalina uma vez que se tenha um bom entendimento do this.
Outro ponto que dificulta o entendimento de inciantes desses métodos são exemplos relativamente complexos de utilização, como o desafio do Douglas Crockford. Entretanto, este serve para lhe deixar seguro de que realmente você está dominando a linguagem.
É, por isso, que recomendo para iniciantes que foquem no aprendizado da palavra reservada This e nos exemplos aqui demonstrados com a função sayThis:
function sayThis(n1,n2){
console.log(this * n1 * n2);
}
Uma vez entendido os exemplos com essa função o entendimento de ilustrações mais complexas seguirá tranquilamente.
Próximos Passsos
Uma vez que aprendemos a utilizar os métodos Call, Apply e Bind completamos a lacuna que faltava para o domínio da variável this. Agora finalmente estamos prestes a mergulhar no mundo da Orientação a Objeto em JavaScript.
- O Segredo dos JavaScript Constructors
- Explorando Objetos em Javascript
- Aprenda Constructors e Prototypes em Javascript