voltar aos artigosobject-oriented js
$cd~/artigos/this-call-apply-bind-contexto-javascript
Object-Oriented JS
série 33 Conceitos JS·parte 18 de 38

this, call, apply & bind: O Pronome Mágico do JavaScript

20 minVanderlei José Alamino

Se você já viu this apontar para o objeto errado, um método quebrar ao ser passado como callback, ou precisou "emprestar" um método de um objeto para outro — este artigo é para você.

O this é citado como uma das partes mais confusas do JavaScript. Mas, uma vez que você entende as regras, nunca mais fica confuso.

snippetJavaScript
const alice = { nome: "Alice", apresentar() { return "Eu sou " + this.nome; } };const bob = { nome: "Bob", apresentar() { return "Eu sou " + this.nome; } }; alice.apresentar();  // "Eu sou Alice"bob.apresentar();    // "Eu sou Bob" // Ventríloquo: função de Bob, voz de Alicebob.apresentar.call(alice);  // "Eu sou Alice"

Neste guia da série 33 Conceitos JS — projeto open source criado por Leonardo Maldonado — você vai aprender:

  • O que this realmente é e por que é determinado na chamada
  • As 5 regras de binding em ordem de prioridade
  • call(), apply() e bind() — quando usar cada um
  • Arrow functions e por que tratam this de forma diferente
  • Armadilhas comuns e como evitá-las

Pré-requisitos: Scope and Closures e Factories & Classes.

this-call-apply-bind-contexto-javascript.meta.jsonjson

// metadados do artigo

{
  "tag": "object-oriented js",
  "readTime": "20 min",
  "author": "Vanderlei José Alamino",
  "source": "33jsconcepts.com",
  "series": "33 Conceitos JS",
  "part": {
    "current": 18,
    "total": 38
  }
}
abrir fonte original
this-call-apply-bind-contexto-javascript.mdmarkdown

Fonte de referência

Conteúdo adaptado a partir do guia original em inglês.

33jsconcepts.com
01

seção

A Analogia do Pronome "Eu"

A palavra "eu" muda de significado dependendo de quem está falando — Alice diz "eu sou desenvolvedora", Bob diz "eu sou designer". A mesma palavra, pessoas diferentes.

snippettext
Alice: "Eu sou desenvolvedora"  →  "Eu" = AliceBob:   "Eu sou designer"       →  "Eu" = Bob

this funciona igual — é o pronome "eu" do JavaScript. Referencia objetos diferentes dependendo de quem está "falando" (qual objeto chama a função).

call, apply e bind são os ventríloquos: permitem controlar quem "eu" se refere, independentemente de qual função está falando.

02

seção

O que é `this`?

this é um identificador especial que se refere ao objeto que está executando o código — tipicamente quem "possui" o método chamado.

this é determinado quando a função é CHAMADA, não quando é definida. Isso é binding dinâmico.

snippetJavaScript
function mostrarThis() { return this; } const obj = { mostrarThis }; mostrarThis();        // undefined (modo estrito)obj.mostrarThis();    // objmostrarThis.call({}); // {}

Essa flexibilidade permite compartilhar métodos, comportamento dinâmico e empréstimo de métodos entre objetos:

snippetJavaScript
function saudar() { return `Olá, eu sou ${this.nome}!`; } const alice = { nome: "Alice", saudar };const bob = { nome: "Bob", saudar }; alice.saudar();  // "Olá, eu sou Alice!"bob.saudar();    // "Olá, eu sou Bob!"
03

seção

As 5 Regras de Binding

Quando o JavaScript precisa descobrir this, segue estas regras em ordem de prioridade (maior sobrescreve menor):

snippettext
1. new Binding          (mais alta)2. Explicit Binding     (call / apply / bind)3. Implicit Binding     (obj.metodo())4. Default Binding      (chamada simples)5. Arrow Functions      (léxico — caso especial)

Arrow functions não têm this próprio — herdam do escopo envolvente (ES2015).

Regra 1: new Binding

Com new, this é o novo objeto criado:

snippetJavaScript
class Pessoa {  constructor(nome) {    this.nome = nome;  }  saudar() { return `Oi, eu sou ${this.nome}`; }} const alice = new Pessoa("Alice");console.log(alice.saudar());  // "Oi, eu sou Alice"

Os 4 passos do new: criar objeto vazio → ligar protótipo → executar construtor com this → retornar objeto.

Regra 2: Explicit Binding

call, apply e bind definem this explicitamente — sobrescrevem binding implícito:

snippetJavaScript
const alice = { nome: "Alice", saudar() { return `Oi, ${this.nome}`; } };const bob = { nome: "Bob" }; alice.saudar.call(bob);  // "Oi, Bob" (explícito vence!)

Regra 3: Implicit Binding

Em obj.metodo(), this é o objeto à esquerda do ponto:

snippetJavaScript
const empresa = {  nome: "TechCorp",  departamento: {    nome: "Engenharia",    obterNome() { return this.nome; }  }}; empresa.departamento.obterNome();  // "Engenharia" (this = departamento)

Armadilha: extrair o método perde o binding:

snippetJavaScript
const saudarFn = usuario.saudar;saudarFn();  // this = undefined (modo estrito)

Comum em callbacks: setTimeout(usuario.saudar, 1000), event handlers, forEach.

Regra 4: Default Binding

Chamada simples funcao() — modo estrito: this = undefined. Sem strict: globalThis.

snippetJavaScript
"use strict";function mostrarThis() { return this; }mostrarThis();  // undefined

Módulos ES6 e classes são automaticamente strict.

Regra 5: Arrow Functions

Sem this próprio — herdam do escopo envolvente:

snippetJavaScript
const usuario = {  nome: "Alice",  saudarRegular: function() { return this.nome; },  saudarArrow: () => this.nome  // this = escopo externo, NÃO usuario!}; usuario.saudarRegular();  // "Alice"usuario.saudarArrow();    // undefined

call, apply e bind não alteram this de arrow functions.

04

seção

Fluxograma de Decisão para `this`

snippettext
É arrow function?  SIM → this = escopo envolvente (FIM)                 NÃO ↓Foi chamada com new?  SIM → this = novo objeto (FIM)                      NÃO ↓Usou call/apply/bind?  SIM → this = objeto especificado (FIM)                       NÃO ↓Foi obj.metodo()?  SIM → this = objeto à esquerda do ponto (FIM)                   NÃO ↓Binding padrão → this = undefined (strict) (FIM)
05

seção

call(), apply() e bind()

MétodoInvoca?ArgumentosRetorna
call()SimIndividuais: call(this, a, b)Resultado
apply()SimArray: apply(this, [a, b])Resultado
bind()NãoIndividuais: bind(this, a)Nova função

Mnemônico: Call = C vírgulas · Apply = Array · Bind = Bind para depois

call() — chamar com this

snippetJavaScript
function saudar(cumprimento, pontuacao) {  return `${cumprimento}, eu sou ${this.nome}${pontuacao}`;} const alice = { nome: "Alice" };saudar.call(alice, "Olá", "!");  // "Olá, eu sou Alice!"

Empréstimo de métodos — usar métodos de array em objetos array-like:

snippetJavaScript
const arrayLike = { 0: "a", 1: "b", 2: "c", length: 3 };const resultado = Array.prototype.slice.call(arrayLike);console.log(resultado);  // ["a", "b", "c"]

apply() — aplicar com array

snippetJavaScript
saudar.apply(alice, ["Olá", "!"]);  // mesmo que call // Clássico: Math.max com arrayconst nums = [5, 2, 9, 1, 7];Math.max.apply(null, nums);  // 9Math.max(...nums);           // moderno — preferível

bind() — ligar para depois

Retorna nova função com this permanentemente ligado:

snippetJavaScript
const saudarAlice = saudar.bind(alice);saudarAlice();           // "Olá, eu sou Alice!"saudarAlice.call(bob);   // ainda "Alice" — bind não pode ser sobrescrito!

Event handlers e timers:

snippetJavaScript
class Toggle {  constructor() { this.ligado = false; }  handleClick() { this.ligado = !this.ligado; }  anexar(btn) {    btn.addEventListener("click", this.handleClick.bind(this));  }}

Aplicação parcial — preencher argumentos antecipadamente:

snippetJavaScript
function multiplicar(a, b) { return a * b; }const dobrar = multiplicar.bind(null, 2);dobrar(5);  // 10
06

seção

Padrões Comuns

Preservar contexto em classes — 3 abordagens

snippetJavaScript
class Jogador {  constructor(nome) {    this.nome = nome;    this.pontuacao = 0;    this.incrementarLigado = this.incrementar.bind(this);  // 1. bind no construtor  }   incrementarLigado() { this.pontuacao++; }   incrementarArrow = () => { this.pontuacao++; };  // 2. arrow como campo   incrementarRegular() { this.pontuacao++; }  // 3. bind inline ou arrow wrapper}

Recomendação: arrow functions como campos de classe (Abordagem 2) — mais limpas para event handlers.

Empréstimo de métodos (legado e DOM)

snippetJavaScript
const divs = document.querySelectorAll("div");// Moderno: [...divs].map(d => d.textContent)// Legado: Array.prototype.map.call(divs, d => d.textContent)
07

seção

Armadilhas Comuns

Contexto perdido em callbacks

snippetJavaScript
class Timer {  constructor() { this.segundos = 0; }  iniciar() {    // ❌ this = undefined    setInterval(function() { this.segundos++; }, 1000);    // ✅ arrow ou bind    setInterval(() => { this.segundos++; }, 1000);  }}

Extrair métodos

snippetJavaScript
const saudar = usuario.saudar;saudar();  // undefinedconst saudarAlice = usuario.saudar.bind(usuario);  // ✅

Funções aninhadas em métodos

snippetJavaScript
calculadora.adicionar([1, 2, 3]);// ❌ forEach(function(n) { this.valor += n; })// ✅ forEach((n) => { this.valor += n; })// ✅ forEach(function(n) { ... }, this)

Arrow como método de objeto

snippetJavaScript
const usuario = {  nome: "Alice",  saudar: () => `Oi, ${this.nome}`  // ❌ this NÃO é usuario};// ✅ use saudar() { ... } sintaxe de método
08

seção

Arrow Functions: A Solução Moderna

Arrow functions não têm this próprio — herdam do escopo envolvente. Perfeitas para callbacks; ruins como métodos de objeto.

snippetJavaScript
const obj = {  nome: "Objeto",  metodoRegular: function() {    function interna() { console.log(this); }      // undefined    const internaArrow = () => console.log(this.nome);  // "Objeto"    interna();    internaArrow();  }};
Caso de usoArrowRegular
Método de objeto❌✅
Callback precisando this externo✅❌ (precisa bind)
Campo de classe (event handler)✅⚠️ precisa bind
Precisa de arguments próprio❌✅

Campos de classe com arrow — padrão React/moderno:

snippetJavaScript
class CaixaDeBusca {  query = "";  handleInput = (e) => {    this.query = e.target.value;  // this sempre = instância  };} const cb = new CaixaDeBusca();const handler = cb.handleInput;handler({ target: { value: "teste" } });  // funciona!

Limitações: não pode usar new, sem arguments próprio (use rest ...args), não é generator.

09

seção

Teste Seu Conhecimento

$ summary --quick

Resumo rápido

  • this é determinado na chamada — binding dinâmico, não na definição
  • 5 regras: new > explicit > implicit > default; arrows são léxicas
  • Regra da esquerda do ponto — obj.metodo() → this = obj
  • Extrair métodos perde this — fonte #1 de bugs
  • call/apply invocam imediatamente; bind retorna função ligada
  • Mnemônico: Call = vírgulas, Apply = Array, Bind = depois
  • Arrows herdam this — perfeitas para callbacks, ruins como métodos de objeto
  • Campos arrow em classes — padrão moderno para event handlers
  • Modo estrito: chamada simples → this = undefined

Créditos da série

Este artigo faz parte de uma série de estudos inspirada no projeto 33 Conceitos JS, criado por Leonardo Maldonado. Todo o conteúdo original está disponível em 33jsconcepts.com.