Pílulas de JavaScript: call e apply
Este é um post de uma série na qual compartilho alguns aprendizados da minha jornada no JavaScript no formato de mini artigos. Veja os posts anteriores no meu perfil!
call()
e apply()
são dois métodos bem similares utilizados para fornecer um valor de this
específico para uma função. Eles tornam possível determinar o contexto de onde ela será executada.
Ambos podem ser invocados diretamente de uma função. Passamos como argumento o objeto que queremos utilizar como valor de this
, além dos outros argumentos que a função recebe, se há algum. Considere a função e o objeto abaixo:
function sayHello() {
console.log('Hello, ' + this.name);
}const user1 = {
name: 'Paula',
hobby: 'witchcraft'
}
Se quisermos aplicar a função sayHello
ao contexto do objeto user1
, podemos fazer isso com call()
. Não chamaremos a função sayHello()
! Apenas faremos referência a ela pelo nome e depois usaremos a notação de ponto para invocar o método de função call()
, passando como argumento o objeto user1
, que é o valor de this
que queremos usar. Vamos testar tudo isso na prática:
sayHello.call(user1);
// 'Hello, Paula'
Note onde está o par de parênteses responsável por fazer a invocação: o método call()
é que executa a função, olhando para dentro do objeto user1
para buscar o valor de this.name
. Nesse momento, a expressão this.name
é substituída por user1.name
, que por sua vez é substituída por 'Paula'
.
Agora, vamos tornar as coisas um pouco mais interessantes! Vamos testar o call()
em uma função que recebe argumentos:
function commentHobby(opinion) {
console.log('I ' + opinion + ' ' + this.hobby);
}const user1 = {
name: 'Paula',
hobby: 'witchcraft'
}
Além de passar o objeto user1
novamente como valor de this
, agora passaremos também o parâmetro recebido pela função commentHobby
na lista de argumentos de call()
. Observe:
commentHobby.call(user1, 'love');
// 'I love witchcraft'
O resultado é exatamente o mesmo; o método call()
chamou a função commentHobby
usando user1
como o valor de this
. Dessa vez, porém, passamos também a string 'love'
para substituir o argumento opinion
.
E se tentarmos usar o método call()
em uma função que está dentro de um objeto — ou seja, um método? Sim: nós podemos pegar emprestado um método de um objeto e aplicá-lo para outro objeto!
Vamos criar um objeto kitty
e um objeto puppy
:
const kitty = {
race: 'cat',
color: 'white',
greet: function() {
console.log('Hi, I'm a ' + this.race);
}
}const puppy = {
race: 'dog',
color: 'black'
}
Se testarmos o método greet
como está, o valor de this
é o objeto kitty
e, portanto, this.race
é substituído pelo seu valor 'cat'
:
kitty.greet();
// 'Hi, I'm a cat'
Mas, supondo que precisamos aproveitar o método greet
para aplicar a puppy
, podemos acessá-lo dentro do objeto kitty
e então usar call()
para invocá-lo no objeto puppy
:
kitty.greet.call(puppy);
// 'Hi, I'm a dog'
A esse ponto, você pode estar se perguntando: mas e o apply()
? Bem, o apply()
tem a exata mesma utilidade: chamar uma função aplicando o valor de this
fornecido, mais quaisquer outros parâmetros necessários. A diferença é que, em vez de parâmetros separados por vírgula, o apply()
recebe os parâmetros adicionais da função em um array.
Vamos testá-lo em uma função com mais de um parâmetro:
function sayWhere(city, country) {
console.log('I'm ' + this.name + ' from ' + city + ', ' + country);
}const user2 = {
name: 'Lola'
}
Vejamos o que acontece se utilizarmos o apply()
para aplicar a função sayWhere
ao contexto do objeto user2
, passando também os argumentos recebidos por sayWhere
em um array:
sayWhere.apply(user2, ['Paris', 'France']);
// 'I'm Lola from Paris, France'
O uso é bem similar, não é? Quando devemos escolher call()
ou apply()
, então?
Isso dependerá do tipo de argumentos necessários na função que desejamos utilizar. O apply()
é especialmente útil quando a função recebe muitos argumentos ou, ainda, quando não temos certeza de quantos argumentos exatamente ela receberá.
Por hoje é só e até a próxima ❤