DevPath · Aprenda a programar ESPTEN

Programação orientada a objetos

this dinâmico: bind, call e apply

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:

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
Coloque isto em prática

O DevPath é um curso prático: aqui você lê a teoria; no app você a coloca em prática com exercícios que rodam de verdade, offline.

Comece grátis no app →
← Herança e encapsulamentoMembros estáticos e métodos privados →