Pular para o conteúdo
Cursos

DevClub

LógicaFront-endBack-endMobile

IA Club

IA na prática
Estudar programaçãoEstudar IA
LiçãoIntermediáriocódigo testado

this em JavaScript: contexto, bind, call e apply

O this é definido na chamada, não na declaração. As quatro regras, a perda de contexto em callback e a diferença de bind, call e apply, com saída real.

Rodolfo Mori7 min de leitura

this é o único nome em JavaScript que não segue o escopo léxico. Todas as outras variáveis são resolvidas pelo lugar onde a função foi escrita; this é resolvido pelo jeito como a função foi chamada. Ler a declaração de uma função não é suficiente para saber quanto vale o this dela.

A regra curta, que resolve a maior parte dos casos: olhe o que está à esquerda do ponto na hora da chamada. Em carrinho.total(), o this é carrinho. Em total(), sem ponto nenhum, o this é undefined em modo estrito — e é aí que nasce o erro mais comum desta lição.

Esta lição fecha o bloco de funções da trilha de JavaScript, depois de escopo e closure. Os dois se resolvem lendo o código; este não.

this é como a palavra “eu”

Se alguém diz “eu vou fechar a loja”, você só descobre quem é “eu” olhando quem falou. A palavra não carrega uma pessoa fixa dentro dela. O this tem essa mesma dependência: seu valor vem da forma como a função foi chamada, o que os livros chamam de call site — o ponto da chamada.

Em carrinho.total(), quem chamou foi carrinho; em total(), não existe objeto à esquerda do ponto. A analogia ajuda a procurar o dono da fala, mas não substitui as regras da linguagem: arrow functions, bind, call e new resolvem o contexto de maneiras próprias. Antes do primeiro bloco, cubra as saídas e responda para cada chamada: “quem está à esquerda do ponto?”. Depois rode. Se a resposta muda quando a função é destacada do objeto, você acabou de ver contexto sendo perdido, não dados sendo apagados.

As quatro regras, numa execução só

Quatro formas de chamar a mesma função, quatro valores de this:

js
function quemSouEu() {
  return this;
}

const loja = { nome: 'Club Store', quemSouEu };

console.log('1. chamada avulsa  :', quemSouEu());
console.log('2. como método     :', loja.quemSouEu().nome);
console.log('3. com call        :', quemSouEu.call({ nome: 'Filial SP' }).nome);
console.log('4. com new         :', new quemSouEu());
1. chamada avulsa : undefined 2. como método : Club Store 3. com call : Filial SP 4. com new : quemSouEu {}

É a mesma função nas quatro linhas — o mesmo objeto na memória, referenciado por dois nomes. Só o formato da chamada mudou.

forma da chamada quem é o this exemplo
com new o objeto recém-criado new Produto()
com call, apply ou bind o que você passou fn.call(loja)
como método, com ponto o objeto antes do ponto loja.quemSouEu()
avulsa undefined em modo estrito quemSouEu()

A ordem da tabela é a ordem de precedência: quando duas se aplicam, ganha a de cima. Uma arrow function não entra nesta tabela — ela não tem this próprio, e é por isso que aparece numa seção separada.

A perda de contexto

Aqui está o comportamento que mais derruba código em produção. O método funciona quando chamado pelo objeto, e para de funcionar quando é guardado numa variável:

js
const cliente = {
  nome: 'Ana Souza',
  saudar() {
    return `Olá, ${this.nome}`;
  },
};

console.log(cliente.saudar());

const saudar = cliente.saudar;
console.log(saudar());
Olá, Ana Souza file:///private/tmp/loja/this-perda.mjs:4 return `Olá, ${this.nome}`; ^

TypeError: Cannot read properties of undefined (reading ‘nome’) at saudar (file:///private/tmp/loja/this-perda.mjs:4:25) at file:///private/tmp/loja/this-perda.mjs:11:13 at ModuleJob.run (node:internal/modules/esm/module_job:439:25)

Node.js v24.16.0

const saudar = cliente.saudar copia a função, não o vínculo com o objeto. O vínculo nunca esteve na função: ele é criado no instante da chamada, pelo ponto. Sem ponto, sem vínculo.

Passar o método como callback é a mesma coisa, escrita de outro jeito — e é a forma em que o problema realmente aparece:

js
const cliente = {
  nome: 'Ana Souza',
  saudar() {
    return `Olá, ${this.nome}`;
  },
};

console.log([7412, 7413].map(cliente.saudar));
file:///private/tmp/loja/this-perda-callback.mjs:4 return `Olá, ${this.nome}`; ^

TypeError: Cannot read properties of undefined (reading ‘nome’) at saudar (file:///private/tmp/loja/this-perda-callback.mjs:4:25) at Array.map (<anonymous>) at file:///private/tmp/loja/this-perda-callback.mjs:8:26 at ModuleJob.run (node:internal/modules/esm/module_job:439:25)

Node.js v24.16.0

O rastro conta a história completa: at saudar na linha 4, chamado por Array.map, chamado pela linha 8. O map invoca o callback sem dono, então this é undefined. É a mesma armadilha de passar método como callback para addEventListener, setTimeout ou forEach.

bind: prender o contexto de uma vez

bind devolve uma função nova com o this fixado. Ela não é a mesma função — é uma cópia amarrada:

js
const cliente = {
  nome: 'Ana Souza',
  saudar() {
    return `Olá, ${this.nome}`;
  },
};

const saudarPreso = cliente.saudar.bind(cliente);

console.log([7412, 7413].map(saudarPreso));
console.log('name:', saudarPreso.name);
console.log('bind gruda para sempre?', saudarPreso.call({ nome: 'Bruno Lima' }));
[ 'Olá, Ana Souza', 'Olá, Ana Souza' ] name: bound saudar bind gruda para sempre? Olá, Ana Souza

Três coisas para guardar. O map funcionou. O nome da função virou bound saudar, o que ajuda a reconhecer no rastro de erro. E o call no fim foi ignorado: bind não se desfaz, nem com outro call, nem com outro bind.

call e apply: emprestar o contexto uma vez

call e apply chamam a função na hora, com o this que você mandar. A única diferença entre eles é o formato dos argumentos:

js
function montarLinha(moeda, sufixo) {
  return `${moeda} ${this.preco.toFixed(2)}${sufixo}`;
}

const teclado = { nome: 'Teclado mecânico', preco: 289.9 };

console.log(montarLinha.call(teclado, 'R$', ' à vista'));
console.log(montarLinha.apply(teclado, ['R$', ' no PIX']));

const precos = [289.9, 149.9, 39.9];
console.log(Math.max.apply(null, precos), Math.max(...precos));
R$ 289.90 à vista R$ 289.90 no PIX 289.9 289.9

call recebe os argumentos soltos; apply recebe um array. O truque Math.max.apply(null, precos) foi por anos a única forma de aplicar uma função variádica a um array — hoje o spread faz o mesmo com menos ruído, como mostra a última linha.

Em código novo, apply praticamente sumiu. call ainda aparece em um idioma específico: emprestar um método de outro protótipo, como Array.prototype.slice.call(algoParecidoComArray).

Arrow function: o this que nem call muda

Uma arrow não tem this próprio. Ela usa o this do escopo onde foi escrita, e esse vínculo é definitivo:

js
const relatorio = {
  loja: 'Club Store',
  emitir() {
    const comArrow = () => this.loja;
    const comFunction = function () {
      return this?.loja;
    };

    return {
      arrow: comArrow(),
      normal: comFunction(),
      arrowComCall: comArrow.call({ loja: 'Filial SP' }),
    };
  },
};

console.log(relatorio.emitir());
{ arrow: 'Club Store', normal: undefined, arrowComCall: 'Club Store' }

A arrow pegou o this de emitir, que era relatorio. A function chamada sem dono ficou com undefined — o ?. evitou a exceção. E o call com um objeto explícito não teve efeito nenhum sobre a arrow: ela devolveu Club Store de novo.

Isso resume por que arrow é ótima como callback e péssima como método. Os outros três buracos da arrow — arguments, prototype e new — estão em arrow function em JavaScript.

O this do topo do arquivo

O mesmo trecho de código dá respostas diferentes conforme o tipo de arquivo. Em CommonJS, sem modo estrito:

js
function quemSouEu() {
  return this === globalThis;
}

console.log('sloppy — this é o global?', quemSouEu());
console.log('typeof this no topo:', typeof this);
sloppy — this é o global? true typeof this no topo: object

Em módulo ES, o mesmo código:

js
function quemSouEu() {
  return this;
}

console.log('módulo ES — this dentro da função:', quemSouEu());
console.log('módulo ES — this no topo         :', this);
módulo ES — this dentro da função: undefined módulo ES — this no topo : undefined

Duas linhas idênticas, dois resultados opostos. É a explicação para o clássico “funciona quando eu colo no console do navegador e quebra no arquivo”: o console é script clássico, o seu arquivo é módulo.

this em classe

Método de classe segue as mesmas regras — e classes são sempre estritas, então a perda de contexto explode em vez de degradar:

js
class Carrinho {
  #itens = [];

  adicionar(item) {
    this.#itens.push(item);
    return this;
  }

  total() {
    return Number(this.#itens.reduce((soma, i) => soma + i.preco, 0).toFixed(2));
  }
}

const carrinho = new Carrinho();
console.log(
  carrinho
    .adicionar({ nome: 'Teclado mecânico', preco: 289.9 })
    .adicionar({ nome: 'Mouse sem fio', preco: 149.9 })
    .total(),
);

const total = carrinho.total;
console.log(total());
439.8 file:///private/tmp/loja/this-classe.mjs:10 return Number(this.#itens.reduce((soma, i) => soma + i.preco, 0).toFixed(2)); ^

TypeError: Cannot read properties of undefined (reading ‘#itens’) at total (file:///private/tmp/loja/this-classe.mjs:10:24) at file:///private/tmp/loja/this-classe.mjs:23:13 at ModuleJob.run (node:internal/modules/esm/module_job:439:25)

Node.js v24.16.0

O encadeamento funcionou porque adicionar devolve this — padrão comum de API fluente, e que só existe por causa dessa palavra. Depois, extrair total para uma variável repetiu a perda de contexto, agora com uma mensagem que cita o campo privado.

A solução moderna é declarar o método como campo com arrow. Campos de instância são inicializados dentro do construtor, então a arrow captura o this da instância:

js
class Carrinho {
  #itens = [{ nome: 'Teclado mecânico', preco: 289.9 }];

  total = () => Number(this.#itens.reduce((soma, i) => soma + i.preco, 0).toFixed(2));
}

const carrinho = new Carrinho();
const total = carrinho.total;

console.log(total());
289.9

O custo é que cada instância ganha a própria cópia da função, em vez de compartilhar uma no protótipo. Para um punhado de objetos, irrelevante; para milhares, prefira bind no construtor.

Regras práticas

  • Não leia a declaração, leia a chamada. Toda pergunta sobre this se responde olhando o que está à esquerda do ponto no momento em que a função é invocada.
  • Método nunca vira callback direto. Escreva setTimeout(() => carrinho.total(), 100) ou setTimeout(carrinho.total.bind(carrinho), 100). Nunca setTimeout(carrinho.total, 100).
  • Arrow para callback, function para método. É a mesma regra por dois ângulos: use arrow quando quiser o this de fora, function quando quiser o da chamada.
  • this.propriedade em função avulsa é sinal de erro de desenho. Se a função precisa de dados, receba por parâmetro — ela fica testável e imune a esse problema inteiro.
  • Quando Cannot read properties of undefined apontar para dentro de um método, suspeite do contexto antes do dado. Muitas vezes o objeto está lá; é o this que sumiu.

Com escopo, hoisting, closure e this resolvidos, o próximo passo é o código assíncrono, onde as suas funções passam a ser chamadas por outra pessoa em outro momento — e as quatro regras desta lição valem exatamente igual. O guia completo de JavaScript mostra a sequência, e o erro Cannot read properties of undefined tem as outras origens dessa mesma mensagem.

  • this
  • bind
  • call
  • apply
  • contexto

Perguntas frequentes

Por que meu método funciona no objeto e quebra dentro do setTimeout?
Porque você passou a função, e não a chamada. O this é decidido pelo que está à esquerda do ponto no momento da chamada; ao passar o método sozinho, esse ponto some. Resolva com bind ou envolvendo em uma arrow que chame o método pelo objeto.
Qual a diferença prática entre call e apply?
Só a forma de passar os argumentos: call recebe soltos, apply recebe um array. Com o operador spread, apply perdeu quase todo o uso — Math.max com spread substitui o idioma clássico com apply e null.
bind pode ser desfeito?
Não. A função retornada por bind ignora qualquer this passado depois, inclusive por outro call ou bind. Se você precisa trocar o contexto em tempo de execução, guarde o método original e chame com call na hora.
Em módulo ES o this do topo é o quê?
undefined. Em CommonJS é module.exports, e em script clássico é window. É a origem mais comum de código que funciona colado no console do navegador e quebra dentro de um arquivo de módulo.

Dúvidas e comentários

Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.

Todo o código deste artigo foi executado em Node 24.16.0, e as saídas exibidas são as reais — como produzimos este conteúdo.

Fontes consultadas

  1. MDN — this — developer.mozilla.org
  2. MDN — Function.prototype.bind — developer.mozilla.org
  3. ECMAScript 2026 — The this Keyword — tc39.es

Continue por aqui