Menu

this em JavaScript: Regras de Binding e Erros Comuns

Entenda como o this realmente funciona em JavaScript: as quatro regras de binding, por que arrow functions são diferentes e como escapar do clássico erro 'this is undefined'.

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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

É 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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

.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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

setTimeout invoca a função como uma chamada comum, e não como c.increment(). Dá pra resolver isso de três formas:

index.js
Output
Click Run to see the output here.

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:

index.js
Output
Click Run to see the output here.

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:

  1. A função é uma arrow function? Então this é o mesmo do escopo onde ela foi criada. Pode ignorar como ela foi chamada.
  2. Foi chamada com new? Então this é o objeto recém-criado.
  3. Foi chamada com .call, .apply ou via função atrelada com bind? Então this é o valor que você passou.
  4. Foi chamada como obj.metodo()? Então this é obj.
  5. Foi chamada como uma função solta fn()? Então this é undefined no 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?

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?

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?

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?

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.

Aprenda a programar com o Coddy

COMEÇAR