Stack Overflow em Português Asked by felipe cardozo on September 26, 2021
Eu fico confuso em utilizar o método call()
. Ele serve para chamar uma função, herdar os parâmetros de uma função ou as propriedades?
Outra dúvida minha é em relação a palavra-chave this
passada como parâmetro para o método call()
, por exemplo:
function A (n1, n2, n3) {
this.n1 = n1;
this.n2 = n2;
this.n3 = n3;
this.numbers = () => {
console.log(`${n1}, ${n2}, ${n3}`)
};
}
function B (n1, n2, n3) {
A.call(this, n1, n2, n3);
this.numbers = () => {
console.log(`${n1}, ${n2}, ${n3}`);
};
}
let a1 = new A(10, 20, 30);
let b1 = new B(100, 200, 300);
console.log(a1.numbers());
console.log(b1.numbers());
A palavra-chave this
que está dentro do método call()
está se referindo a quêm? Ao construtor A
ou B
? E por que ela deve estar alí passada como parâmetro? Eu falo isso porque o MDN menciona que o uso dela é opcional no método call()
.
No primeiro exemplo é usado o this
como parâmetro do método call()
, mas neste segundo exemplo não:
function A (n1, n2, n3) {
this.n1 = n1;
this.n2 = n2;
this.n3 = n3;
this.numbers = () => {
console.log(`${n1}, ${n2}, ${n3}`)
};
}
function B (n1, n2, n3) {
A.call(n1, n2, n3);
this.numbers = () => {
console.log(`${n1}, ${n2}, ${n3}`);
};
}
let a1 = new A(10, 20, 30);
let b1 = new B(100, 200, 300);
console.log(a1.numbers());
console.log(b1.numbers());
Então tanto faz usar ela, visto que os mesmos resultados serão retornados? Que efeito isso vai causar em usar e não usar?
Em JavaScript, o this
é bem dinâmico, variando conforme o contexto. E call
é uma das maneiras de se controlar isso. Por exemplo, se eu tiver uma função que retorna this
:
function bla() {
return this;
}
console.log(bla());
// se rodar no snippet do site, como é em um browser, o this é igual a window
console.log(bla() == window); // true
Se não estiver em modo strict (no qual this
é undefined
), a função retorna o objeto global (window
, se estiver no browser).
Mas usando call
, podemos alterar o this
que a função enxerga:
function bla() {
return this;
}
let sereiUmNovoThis = { id: 1, nome: 'Novo this' };
console.log(bla.call(sereiUmNovoThis)); // { id: 1, nome: 'Novo this' }
//---------------------------------------------
// exemplo com parâmetro
function imprimeProp(propName) {
console.log(this[propName]);
}
imprimeProp.call({ id: 1 }, 'id'); // 1
imprimeProp.call([1, 2, 3], 'length'); // 3
Repare que se a função tiver parâmetros, estes também são passados para call
. De forma geral, ao fazer:
funcao.call(p0, p1, p2, p3, ..pn);
p0
se torna o this
, e os demais (de p1
até pn
) são passados como argumentos para a função.
Ou seja, imprimeProp.call({ id: 1 }, 'id');
faz com que o objeto { id: 1 }
seja o this
, e a string 'id'
seja o primeiro argumento da função (ou seja, propName
neste caso terá o valor 'id'
).
No segundo caso, o array [1, 2, 3]
é o this
e a string 'length'
é o valor de propName
.
A documentação cita vários exemplos de uso, então não vou ficar repetindo um a um.
Quanto ao seu exemplo, vamos modificá-lo um pouco para entender melhor o que aconteceu:
function A (n1, n2, n3) {
console.log(`recebido: ${this}, ${n1}, ${n2}, ${n3}`);
this.v1 = n1;
this.v2 = n2;
this.v3 = n3;
this.numbers = () => {
console.log(`${n1}, ${n2}, ${n3} - ${this.v1}, ${this.v2}, ${this.v3}`);
};
}
function B (n1, n2, n3) {
A.call(n1, n2, n3);
this.numbers = () => {
console.log(`${n1}, ${n2}, ${n3} - ${this.v1}, ${this.v2}, ${this.v3}`);
};
}
console.log('criando a1');
let a1 = new A(10, 20, 30);
console.log(a1);
a1.numbers();
console.log('criando b1');
let b1 = new B(100, 200, 300);
console.log(b1);
b1.numbers();
A saída é:
criando a1
recebido: [object Object], 10, 20, 30
{
"v1": 10,
"v2": 20,
"v3": 30,
"numbers": () => {
console.log(`${n1}, ${n2}, ${n3} - ${this.v1}, ${this.v2}, ${this.v3}`);
}
}
10, 20, 30 - 10, 20, 30
criando b1
recebido: 100, 200, 300, undefined
{
"numbers": () => {
console.log(`${n1}, ${n2}, ${n3} - ${this.v1}, ${this.v2}, ${this.v3}`);
}
}
100, 200, 300 - undefined, undefined, undefined
Vamos por partes, primeiro vejamos o que acontece com a1
:
new A(10, 20, 30)
chama a função A
passando os valores 10 para n1
, 20 para n2
e 30 para n3
this.v1
, this.v2
e this.v3
numbers
, que imprime os valores de n1
, n2
e n3
, além de this.v1
, this.v2
e this.v3
No caso, os valores estão corretos, conforme o esperado. Eu imprimi o próprio a1
e podemos ver os valores de this.v1
, this.v2
e this.v3
, iguais aos que foram passados. O método numbers
também imprimiu os valores corretamente.
Agora, o que acontece com b1
?
new B(100, 200, 300)
chama a função B
passando os valores 100 para n1
, 200 para n2
e 300 para n3
A.call(n1, n2, n3)
. Ou seja, n1
é o primeiro argumento, então nesse caso ele é o this
(podemos ver isso em console.log(`recebido: ${this}, ${n1}, ${n2}, ${n3}`)
, veja que this
é igual a 100). Com isso, n1
recebeu o valor 200, n2
recebeu 300 e n3
ficou undefined
.numbers
imprime os valores de n1
, n2
e n3
, mas não os valores que A
recebeu, e sim os que B
recebeu (pois este é definido dentro de B
). Por isso que ele imprime 100, 200, 300
corretamente.b1
, não aparecem os valores de v1
, v2
e v3
. Por isso ao imprimir this.v1
, this.v2
e this.v3
, todos estão undefined
E se passarmos o this
como o primeiro argumento de call
?
function A (n1, n2, n3) {
console.log(`recebido: ${this}, ${n1}, ${n2}, ${n3}`);
this.v1 = n1;
this.v2 = n2;
this.v3 = n3;
this.numbers = () => {
console.log(`${n1}, ${n2}, ${n3} - ${this.v1}, ${this.v2}, ${this.v3}`);
};
}
function B (n1, n2, n3) {
A.call(this, n1, n2, n3); // passei o this como argumento
this.numbers = () => {
console.log(`${n1}, ${n2}, ${n3} - ${this.v1}, ${this.v2}, ${this.v3}`);
};
}
console.log('criando a1');
let a1 = new A(10, 20, 30);
console.log(a1);
a1.numbers();
console.log('criando b1');
let b1 = new B(100, 200, 300);
console.log(b1);
b1.numbers();
Agora a saída é:
criando a1
recebido: [object Object], 10, 20, 30
{
"v1": 10,
"v2": 20,
"v3": 30,
"numbers": () => {
console.log(`${n1}, ${n2}, ${n3} - ${this.v1}, ${this.v2}, ${this.v3}`);
}
}
10, 20, 30 - 10, 20, 30
criando b1
recebido: [object Object], 100, 200, 300
{
"v1": 100,
"v2": 200,
"v3": 300,
"numbers": () => {
console.log(`${n1}, ${n2}, ${n3} - ${this.v1}, ${this.v2}, ${this.v3}`);
}
}
100, 200, 300 - 100, 200, 300
Agora sim. Ao fazer A.call(this, n1, n2, n3)
, estou passando o this
, que no caso refere-se a B
(já que estou dentro da função B
). Ou seja, estou chamando A
, mas ela considera que B
é o this
. Por isso os valores de this.v1
, this.v2
e this.v3
são setados corretamente em B
.
Aliás, isso é exatamente o primeiro exemplo da documentação. É uma forma de B
"reaproveitar" a função construtora A
. Aliás, como agora estamos passando o this
correto, a função B
nem precisaria redefinir numbers
(pois este já seria criado corretamente dentro de A
):
function A (n1, n2, n3) {
console.log(`recebido: ${this}, ${n1}, ${n2}, ${n3}`)
this.v1 = n1;
this.v2 = n2;
this.v3 = n3;
this.numbers = () => {
console.log(`${n1}, ${n2}, ${n3} - ${this.v1}, ${this.v2}, ${this.v3}`);
};
}
function B (n1, n2, n3) {
A.call(this, n1, n2, n3);
}
let b = new B(1, 2, 3);
b.numbers();
Como eu passei this
para A
(e este é B
), então this.numbers
define numbers
em B
, e tudo funciona como o esperado.
Talvez o que mais confunda neste caso é o fato do this
significar uma coisa diferente a cada momento.
Quanto ao primeiro argumento ser opcional, a documentação cita que de fato é, mas aí você não pode passar nenhum outro argumento (teria que ser apenas A.call()
). Neste caso, o this
passa a ser o objeto global (ou undefined
se estiver no modo strict).
function bla() {
return this;
}
// se rodar no snippet do site, como é em um browser, o this é igual a window
console.log(bla.call() == window); // true
function blaStrict() {
"use strict";
return this;
}
console.log(blaStrict.call()); // undefined
Não é algo diretamente relacionado à pergunta, mas na verdade, da forma que você fez, uma nova função numbers
sempre é criada cada vez que você cria um novo A
ou B
. O ideal seria declarar a função no prototype
de A
, ou usar a sintaxe de classes do ES6. Como não é o foco da pergunta, deixo algumas referências: essa, essa, essa e essa.
Correct answer by hkotsubo on September 26, 2021
Você só precisa usar o call
se quiser redefinir a qual objeto um método está vinculado para aquela execução.
O this
em js é dinâmico. Sendo assim você pode transferir métodos entre objetos e tudo funciona.
O call
te permite executar um método no contexto de um objeto, sem precisar vincular esse método ao objeto. Esse exemplo deve esclarecer:
let A = {
x: 2,
dobro() {
return this.x * 2
}
};
let B = {
x: 3
};
let C = {
x: 4
};
B.dobro = A.dobro;
console.log('Dobro of B.x:', B.dobro());
console.log('Dobro of C.x:', A.dobro.call(C));
console.log('Attributes of B:', Object.keys(B));
console.log('Attributes of C:', Object.keys(C));
Como você pode ver, o dobro é apresentado corretamente para os objetos B e C, mas o C não incorporou o método. Isso previne poluição de objetos ou permite meta-programação de formas mais elaboradas.
Answered by Aurium on September 26, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP