this depende de COMO é chamado
A regra mais importante de this: não importa onde você definiu a função,
importa como você a invoca. Pense em this como um assento vazio que se preenche
no momento da chamada, conforme o que houver "à esquerda do ponto".
const pessoa = {
nome: "Ana",
saudar() {
return "Olá, sou " + this.nome;
},
};
pessoa.saudar(); // this = pessoa -> "Olá, sou Ana"
const solta = pessoa.saudar;
solta(); // this = undefined -> erro ou "undefined"!
Ao guardar o método em solta e chamá-lo "puro", já não há nada à
esquerda do ponto: this se perde. Essa é a temida "perda de this".
Onde isso te morde na prática
Acontece sempre que você passa um método como callback e outro código o chama por você:
setTimeout(pessoa.saudar, 100); // this se perde
botao.addEventListener("click", obj.tratar); // this se perde
const { saudar } = pessoa; // desestruturar também o desconecta
bind: fixa o this e devolve uma função nova
bind não chama a função: cria uma cópia atada a um this fixo, que já
nunca muda, mesmo que você a invoque pura.
const saudarAna = pessoa.saudar.bind(pessoa);
saudarAna(); // "Olá, sou Ana" (mesmo chamada pura)
setTimeout(saudarAna, 100); // agora sim funciona
call e apply: invocam AGORA fixando o this
Diferentemente de bind, estes executam a função imediatamente indicando qual
this usar. A única diferença entre ambos é como você passa os argumentos:
call: argumentos soltos, separados por vírgulas.apply: argumentos dentro de um array.
function apresentar(saudacao, sinal) {
return saudacao + ", sou " + this.nome + sinal;
}
const ana = { nome: "Ana" };
apresentar.call(ana, "Olá", "!"); // call: args soltos
apresentar.apply(ana, ["Olá", "!"]); // apply: args em array
// Ambos -> "Olá, sou Ana!"
Truque para lembrar: apply usa array.
Solução moderna: campos de classe com função de seta
As funções de seta não têm seu próprio this: elas o herdam de onde foram
criadas. Se você define um método como campo com seta, ele fica "soldado" à
instância e nunca perde o this, mesmo que você o passe como callback:
class Botao {
constructor(texto) {
this.texto = texto;
}
// campo de classe com seta: this sempre será esta instância
clique = () => {
return "Pressionado: " + this.texto;
};
}
const b = new Botao("Aceitar");
const fn = b.clique;
fn(); // "Pressionado: Aceitar" (o this não se perde)
Exemplos
A perda de this e como bind a conserta
const contador = {
valor: 10,
ler() {
return this.valor;
},
};
const solta = contador.ler;
const atada = contador.ler.bind(contador);
console.log("Atada:", atada()); // 10
console.log("Solta this:", solta === contador.ler);
call vs apply: mesma função, forma diferente de passar args
function faixa(min, max) {
return this.nome + " entre " + min + " e " + max;
}
const sensor = { nome: "Temperatura" };
console.log(faixa.call(sensor, 0, 100));
console.log(faixa.apply(sensor, [0, 100]));
Seta em campo de classe: this soldado à instância
class Timer {
segundos = 0;
tick = () => {
this.segundos++;
return this.segundos;
};
}
const t = new Timer();
const fn = t.tick; // o "desconectamos"
console.log(fn()); // 1 (continua funcionando)
console.log(fn()); // 2