Em JavaScript, a palavra-chave this comporta-se de maneira diferente em comparação com linguagens orientadas a objetos tradicionais baseadas em classes. O valor de this não é determinado por onde a função foi declardaa, mas sim por como ela é invocada em tempo de execução. Esse objeto de contexto é frequentemente chamado de "contexto de execução".
Para dominar o comportamento do this, é fundamental compreender as cinco regras prinicpais de vinculação (binding) utilizadas pelo motor do JavaScript.
1. Vinculação Padrão (Escopo Global)
Quando uma função é invocada de forma independente, sem nenhum objeto de contexto à sua esquerda, a vinculação padrão é aplicada. Em navegadores, this aponta para o objeto global window. No Node.js, aponta para global.
function exibirStatus() {
this.statusGlobal = "ativo";
}
exibirStatus();
console.log(statusGlobal); // "ativo"
Atenção ao Strict Mode: Se o código acima for executado em modo estrito ("use strict";), o this não assumirá o objeto global, mas sim undefined. Tentar atribuir propriedades a undefined resultará em um erro TypeError.
2. Vinculação Implícita
Quando uma função é chamada como um método de um objeto, o contexto de execução é definido como o objeto que está à esquerda do ponto. Nesse cenário, this é vinculado implicitamente a esse objeto.
function iniciarMotor() {
this.ligado = true;
}
const veiculo = {
ligado: false,
iniciar: iniciarMotor
};
veiculo.iniciar();
console.log(veiculo.ligado); // true
Como a função iniciarMotor foi envocada através de veiculo.iniciar(), o this dentro da função refere-se ao objeto veiculo.
3. Vinculação Explícita
JavaScript fornece métodos nativos que permitem forçar a vinculação do this a um objeto específico no momento da chamada. São eles: call(), apply() e bind().
function apresentar(nome, idade) {
console.log(`Nome: ${this.nome}, Idade: ${idade}`);
}
const usuario = { nome: "Carlos" };
// call() passa os argumentos separados por vírgula
apresentar.call(usuario, 30);
// apply() passa os argumentos em um array
apresentar.apply(usuario, [25]);
// bind() retorna uma nova função com o this fixado
const apresentarCarlos = apresentar.bind(usuario);
apresentarCarlos(40);
4. Vinculação com new (Construtores)
Quando uma função é precedida pela palavra-chave new, o JavaScript executa várias ações, incluindo a criação de um novo objeto vazio e a vinculação do this a esse novo objeto. Funções projetadas para isso são chamadas de funções construtoras.
function ConfigurarTema(cor, fonte) {
this.cor = cor;
this.fonte = fonte;
}
const temaEscuro = new ConfigurarTema("#000000", "Arial");
console.log(temaEscuro.cor); // "#000000"
console.log(temaEscuro.fonte); // "Arial"
O uso de new garante que o this aponte para a instância recém-criada (temaEscuro), e não para o escopo global.
5. Perda de Contexto em Callbacks e a Solução com Arrow Functions
Um problema comum ocorre quando funções são passadas como callbacks (como em setTimeout ou eventos de DOM). O contexto do this é perdido e recai sobre a vinculação padrão (escopo global).
function Tarefa(descricao) {
this.descricao = descricao;
this.executar = function() {
console.log(`Executando: ${this.descricao}`);
};
this.agendar = function() {
setTimeout(function() {
// Erro: this aponta para o objeto global (window), não para a instância de Tarefa
this.executar();
}, 1000);
};
}
const minhaTarefa = new Tarefa("Limpar banco de dados");
minhaTarefa.agendar(); // Falha com TypeError
Existem duas formas clássicas de resolver esse problema:
Solução A: Variável de Contexto (Self/That)
Antes de entrar no callback, armazene a referência do this em uma variável local.
function Tarefa(descricao) {
this.descricao = descricao;
this.executar = function() {
console.log(`Executando: ${this.descricao}`);
};
this.agendar = function() {
const self = this; // Armazena o contexto atual
setTimeout(function() {
self.executar(); // Usa a variável self
}, 1000);
};
}
Solução B: Arrow Functions (ES6)
Funções de seta não possuem seu próprio this. Elas herdam o this do escopo léxico onde foram definidas, o que as torna ideais para callbacks.
function Tarefa(descricao) {
this.descricao = descricao;
this.executar = function() {
console.log(`Executando: ${this.descricao}`);
};
this.agendar = function() {
setTimeout(() => {
// O this aqui é o mesmo do método agendar, ou seja, a instância de Tarefa
this.executar();
}, 1000);
};
}
const minhaTarefa = new Tarefa("Limpar banco de dados");
minhaTarefa.agendar(); // Funciona perfeitamente