this é definido na hora da chamada
Boa parte da confusão com o this em JavaScript vem de uma suposição errada: achar que o this depende de onde a função foi definida. Não é bem assim. O valor de this depende de como a função é chamada.
Veja a mesma função sendo chamada de duas formas diferentes:
Mesma função, this diferente. Na primeira chamada, user está antes do ponto, então this é user. Na segunda chamada, não tem nada antes do ponto, então this é undefined. A função em si não mudou — o que mudou foi o local da chamada.
Guarde essa ideia: para descobrir o valor de this, olhe para a chamada, não para a definição.
As quatro regras de binding do this
Existem quatro formas de o this ser definido. Praticamente toda dúvida sobre this se resolve descobrindo qual dessas regras está em jogo.
1. Chamada como método: obj.fn()
Quando uma função é chamada como propriedade de um objeto, o this é esse objeto:
O que vem antes do ponto vira o this. É só isso.
2. Chamada simples: fn()
Quando você chama a função sem nenhum objeto antes dela, o this é undefined no strict mode (que módulos e classes já ativam automaticamente) ou o objeto global no sloppy mode:
É daqui que nascem aqueles temidos erros de this is undefined. Quando você "arranca" um método do objeto ao qual ele pertence, a chamada deixa de ser uma chamada de método e vira uma chamada de função comum:
Sem o counter. antes da chamada, não rola binding nenhum. A função simplesmente não lembra de qual objeto ela veio.
3. Binding explícito: .call(), .apply() e .bind()
Dá pra forçar o valor de this pra qualquer coisa que você quiser:
.call e .apply executam a função na hora; a diferença está só em como você passa os argumentos. Já o .bind devolve uma nova função com o this fixado de vez — muito útil quando você precisa passar um callback.
4. Chamada com new: new Fn()
Quando você chama uma função com new, o JavaScript cria um objeto novinho e amarra ele ao this:
Por baixo dos panos, as classes usam esse mesmo mecanismo. A gente vai falar delas em um capítulo mais pra frente.
Arrow functions não têm this próprio
As arrow functions quebram as regras acima — e isso é de propósito. Elas simplesmente não fazem o binding do this; em vez disso, herdam o valor do escopo ao redor, congelado no momento em que a arrow é definida:
A arrow function no nível de módulo captura o this do módulo, que é undefined. Mesmo chamando como user.arrow(), a arrow se recusa a trocar o this.
Pode parecer um bug, mas é justamente esse o propósito. As arrow functions brilham quando usadas dentro de métodos, nos casos em que você quer preservar o this externo:
A arrow function dentro do setInterval herda o this do start, que foi chamado como timer.start(). Por isso this.seconds funciona direitinho. Se fosse uma function tradicional ali, ela teria seu próprio this (o que o setInterval passar) e tudo quebraria.
Regra prática: use arrow functions para callbacks dentro de métodos. Para os métodos em si, use funções tradicionais.
A cilada clássica do callback
Essa é a forma mais comum do this sair pelos trilhos no JavaScript. Você passa um método como callback e ele perde o binding:
setTimeout invoca a função como uma chamada comum, e não como c.increment(). Dá pra resolver isso de três formas:
As três formas funcionam. Normalmente, envolver em arrow function é a alternativa mais clara.
this no nível global
No nível mais externo do código, o valor de this depende do ambiente em que ele roda:
- Script de navegador (não-module):
thiséwindow. - Módulo ES (inclusive na maior parte do código moderno empacotado por bundlers):
thiséundefined. - Módulo CommonJS do Node.js:
thisémodule.exports.
Se você precisa de uma referência confiável ao objeto global independente do ambiente, use globalThis:
Na prática, evite depender do this no nível superior. Quando você realmente precisar do objeto global, use globalThis. Fora isso, passe os valores explicitamente.
Uma árvore de decisão rápida
Quando bater a dúvida sobre o que this vai ser, percorra esta lista na ordem:
- A função é uma arrow function? Então
thisé o mesmo do escopo onde ela foi criada. Pode ignorar como ela foi chamada. - Foi chamada com
new? Entãothisé o objeto recém-criado. - Foi chamada com
.call,.applyou via função atrelada combind? Entãothisé o valor que você passou. - Foi chamada como
obj.metodo()? Entãothiséobj. - Foi chamada como uma função solta
fn()? Entãothiséundefinedno strict mode.
Essa escadinha, nessa ordem, resolve qualquer caso.
A seguir: funções de ordem superior
Agora que this deixou de ser um bicho de sete cabeças, você já está pronto para o padrão que torna o JavaScript tão expressivo: tratar funções como valores e passá-las por aí. No próximo tópico vamos falar de funções de ordem superior — aquelas que recebem ou retornam outras funções — e como elas sustentam os métodos de array, os handlers de eventos e praticamente todo código JavaScript do mundo real.
Perguntas frequentes
A que o this se refere em JavaScript?
this se refere em JavaScript?O this aponta para o objeto sobre o qual a função foi chamada — e não para onde ela foi definida. Em user.greet(), o this é user. Já numa chamada solta como greet(), o this fica undefined no strict mode (ou vira o objeto global no modo sloppy). A regra de ouro é: o que importa é o local da chamada, não o local da definição.
Por que o this está undefined dentro da minha função?
this está undefined dentro da minha função?Provavelmente você tirou um método do objeto dele e chamou isoladamente, ou passou como callback. Algo como const fn = user.greet; fn(); perde o binding — não tem objeto antes do ponto no momento da chamada. Dá para resolver com .bind(user), envolvendo numa arrow function, ou chamando direto como user.greet().
Como o this se comporta diferente em arrow functions?
this se comporta diferente em arrow functions?Arrow functions não têm o próprio this. Elas herdam o this do escopo ao redor no momento em que são definidas. Isso torna elas perfeitas para callbacks dentro de métodos, quando você quer preservar o this externo. Também significa que .call(), .apply() e .bind() não conseguem mudar o this de uma arrow function.
O que é o this no topo de um script?
this no topo de um script?Num script comum rodando no navegador, o this no nível mais externo é o objeto window. Num módulo ES, é undefined. Em módulos CommonJS do Node.js, é o module.exports. Para ter uma referência que funciona em qualquer ambiente, use globalThis, que sempre aponta para o objeto global.