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.
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:
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());É 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:
const cliente = {
nome: 'Ana Souza',
saudar() {
return `Olá, ${this.nome}`;
},
};
console.log(cliente.saudar());
const saudar = cliente.saudar;
console.log(saudar());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:
const cliente = {
nome: 'Ana Souza',
saudar() {
return `Olá, ${this.nome}`;
},
};
console.log([7412, 7413].map(cliente.saudar));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:
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' }));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:
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));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:
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());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:
function quemSouEu() {
return this === globalThis;
}
console.log('sloppy — this é o global?', quemSouEu());
console.log('typeof this no topo:', typeof this);Em módulo ES, o mesmo código:
function quemSouEu() {
return this;
}
console.log('módulo ES — this dentro da função:', quemSouEu());
console.log('módulo ES — this no topo :', this);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:
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());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:
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());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
thisse 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)ousetTimeout(carrinho.total.bind(carrinho), 100). NuncasetTimeout(carrinho.total, 100). - Arrow para callback,
functionpara método. É a mesma regra por dois ângulos: use arrow quando quiser othisde fora,functionquando quiser o da chamada. this.propriedadeem 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 undefinedapontar para dentro de um método, suspeite do contexto antes do dado. Muitas vezes o objeto está lá; é othisque 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.
Perguntas frequentes
Por que meu método funciona no objeto e quebra dentro do setTimeout?
Qual a diferença prática entre call e apply?
bind pode ser desfeito?
Em módulo ES o this do topo é o quê?
Dúvidas e comentários
Travou em algum passo? Pergunte aqui — a equipe e outros alunos respondem.
Entrar para perguntarÉ o mesmo login gratuito dos cursos.
Nenhuma dúvida por aqui ainda — a primeira pode ser a sua.
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
- MDN — this — developer.mozilla.org
- MDN — Function.prototype.bind — developer.mozilla.org
- ECMAScript 2026 — The this Keyword — tc39.es


