Aprenda / JavaScript do zero

IntermediárioAula 10 de 1616 min0% da trilha

this, call, apply e bind

this não é fixo — depende de como a função é chamada, e call/apply/bind existem pra você controlar isso na mão.

Na lição de funções você viu que arrow function não tem this próprio. Mas isso só faz sentido de verdade quando você entende o que determina o this de uma função normal em primeiro lugar — e é isso que costuma gerar o bug mais clássico de JavaScript orientado a objeto.

this depende de como a função é chamada, não de onde foi escrita

const usuario = {
  nome: "Ana",
  saudar() {
    console.log(`Olá, ${this.nome}`);
  },
};

usuario.saudar(); // "Olá, Ana" — this é usuario, porque a chamada foi usuario.saudar()

const saudarSolto = usuario.saudar;
saudarSolto(); // "Olá, undefined" — a função é a mesma, mas a ligação com usuario se perdeu

saudarSolto é literalmente a mesma função que usuario.saudar. O que muda é como ela é chamada: quando você chama objeto.metodo(), this vira objeto. Quando chama a função "solta", sem nenhum objeto antes do ponto, essa ligação some.

O bug clássico: perder o this num callback

const usuario = {
  nome: "Ana",
  saudar() {
    console.log(`Olá, ${this.nome}`);
  },
};

// parece inofensivo, mas quebra:
setTimeout(usuario.saudar, 100); // "Olá, undefined"
// setTimeout chama a função sozinha, sem o "usuario." na frente —
// é exatamente o mesmo problema de saudarSolto() acima

Passar usuario.saudar como argumento entrega só a função, sem o objeto que a acompanhava. Quando setTimeout chama essa função depois, não existe mais nenhum usuario. na frente pra definir o this.

call e apply — chamar a função definindo o this na mão

function saudar(saudacao) {
  console.log(`${saudacao}, ${this.nome}`);
}

const ana = { nome: "Ana" };
const bia = { nome: "Bia" };

saudar.call(ana, "Olá");    // "Olá, Ana"  — chama agora, this = ana, argumentos um a um
saudar.apply(bia, ["Oi"]);  // "Oi, Bia"   — igual a call, mas argumentos dentro de um array

call e apply fazem exatamente a mesma coisa — a única diferença é como você passa os argumentos extras (separados em call, dentro de um array em apply). Os dois chamam a função imediatamente.

bind — não chama agora, devolve uma função nova com this travado

const saudarAna = saudar.bind(ana);
saudarAna("E aí"); // "E aí, Ana" — this já vem travado em ana, não importa como saudarAna for chamada depois

setTimeout(usuario.saudar.bind(usuario), 100); // "Olá, Ana" — corrige o bug do exemplo anterior

O outro jeito de corrigir: arrow function dentro do método

Você já viu esse padrão na lição de funções — vale reforçar por quê ele funciona. Uma arrow function criada dentro de um método herda o this desse método, então não perde a ligação com o objeto:

const usuario2 = {
  nome: "Ana",
  saudarComAtraso() {
    setTimeout(() => {
      console.log(`Olá, ${this.nome}`); // this aqui é herdado de saudarComAtraso — que é usuario2
    }, 100);
  },
};

usuario2.saudarComAtraso(); // "Olá, Ana"

Exercício

Dado const carrinho = { itens: ["mouse", "teclado"], contarItens() { return this.itens.length; } }, o código setTimeout(carrinho.contarItens, 100) quebra (this perdido). Corrija usando bind.

Quiz rápido

O que determina o valor de this dentro de uma função normal?
Qual a diferença entre call/apply e bind?