A palavra-chave this permite que uma função identifique o contexto no qual está sendo executada. Diferente de outras linguagens, no JavaScript o valor de this é determinado em tempo de execução, não no momento da escrita do código. O contexto depende exclusivamente da forma como a função é invocada.
Existem quatro mecanismos de vinculação do this: vinculação padrão, vinculação implícita, vinculação explícita (através de call, apply ou bind) e vinculação via operador new.
Vinculação Padrão
Quando uma função é chamada sem nenhum contexto específico, o this aponta para o objeto global.
function calcular() {
console.log(this.valor);
}
var valor = 10;
calcular(); // 10 — this aponta para o objeto global
Vinculação Implícita
Ao chamar uma função como método de um objeto, o this passa a referenciar esse objeto.
function exibir() {
console.log(this.nome);
}
var pessoa = {
nome: "Carlos",
exibir: exibir
};
pessoa.exibir(); // "Carlos" — this aponta para pessoa
Em cadeias de objetos, o this se refere ao objeto imediatamente anterior ao método chamado:
function exibir() {
console.log(this.idade);
}
var config = {
idade: 28,
dados: {
idade: 35,
exibir: exibir
}
};
config.dados.exibir(); // 35 — this aponta para dados
Quando uma referência à função é armazenada em uma variável, a vinculação implícita é perdida:
function exibir() {
console.log(this.valor);
}
var dados = {
valor: 42,
exibir: exibir
};
var copia = dados.exibir; // copia agora referencia a função diretamente
var valor = "global";
copia(); // "global" — sem contexto implícito, this aponta para o global
O mesmo ocorre quando passamos uma função como argumento:
function processar(callback) {
callback(); // chamada sem contexto
}
var dados = {
valor: 99,
executar: function() { console.log(this.valor); }
};
var valor = "padrão";
processar(dados.executar); // "padrão"
Funções de calback como setTimeout também perdem o contexto:
function exibir() {
console.log(this.id);
}
var elemento = {
id: "botao-principal",
exibir: exibir
};
setTimeout(elemento.exibir, 50); // undefined — this é o objeto global
Vinculação Explícita com call e apply
É possível forçar um contexto específico usando call ou apply:
function saudacao() {
console.log(this.mensagem);
}
var config = {
mensagem: "Olá mundo"
};
saudacao.call(config); // "Olá mundo"
Uma vez vinculada explicitamente, o this não pode ser alterado por chamadas subsequentes — isso é chamado de vinculação rígida (hard binding):
function mostrar() {
console.log(this.numero);
}
var alvo = { numero: 7 };
var funcaoRigida = function() {
mostrar.call(alvo);
};
funcaoRigida(); // 7
setTimeout(funcaoRigida, 100); // 7
funcaoRigida.call({ numero: 99 }); // 7 — vinculação rígida prevalece
Uma aplicação prática é criar funções auxiliares que encapsulam um contexto fixo:
function somar(valor) {
console.log(this.base, valor);
return this.base + valor;
}
var operador = { base: 10 };
function criarWrapper(fn, contexto) {
return function() {
return fn.apply(contexto, arguments);
};
}
var somarDez = criarWrapper(somar, operador);
var resultado = somarDez(5); // 10 5
console.log(resultado); // 15
O JavaScript oferece o método nativo bind que realiza a mesma operação:
function multiplicar(fator) {
console.log(this.base, fator);
return this.base * fator;
}
var operador = { base: 8 };
var multiplicarPorOito = multiplicar.bind(operador);
var res = multiplicarPorOito(3); // 8 3
console.log(res); // 24
Muitas funções nativas aceitam um parâmetro de contexot. Por exemplo, forEach recebe como segundo argumento o valor de this:
function logItem(item) {
console.log(item, this.prefixo);
}
var logger = { prefixo: "ITEM:" };
[10, 20, 30].forEach(logItem, logger);
// 10 ITEM: 20 ITEM: 30 ITEM:
Vinculação via operador new
Ao utilizar new para invocar uma função, um novo objeto é criado e vinculado ao this:
function Produto(preco) {
this.preco = preco;
}
var item = new Produto(50);
console.log(item.preco); // 50
Ordem de precedência
As formas de vinculação possuem prioridades: new > vinculação explícita > vinculação implícita > vinculação padrão.
function exibir() {
console.log(this.valor);
}
var ctx1 = { valor: 100, exibir: exibir };
var ctx2 = { valor: 200, exibir: exibir };
ctx1.exibir(); // 100
ctx2.exibir(); // 200
ctx1.exibir.call(ctx2); // 200 — explícita supera implícita
A vinculação com new tem a maior prioridade:
function Construtor(valor) {
this.valor = valor;
}
var contexto = { Construtor: Construtor };
var instancia = new contexto.Construtor(55);
console.log(instancia.valor); // 55 — new prevalece
Casos especiais
Passar null ou undefined para call ou apply ativa a vinculação padrão:
function verificar() {
console.log(this.x);
}
var x = 42;
verificar.call(null); // 42 — vinculação padrão
Isso é útil para curry de funções usando apply:
function concatenar(a, b, c) {
return a + b + c;
}
var resultado = concatenar.apply(null, ["A", "B", "C"]);
console.log(resultado); // "ABC"
Para evitar efeitos colaterais ao usar null, pode-se criar um objeto vazio seguro:
var vazio = Object.create(null);
function somar(a, b) { return a + b; }
var res = somar.apply(vazio, [7, 8]);
console.log(res); // 15
Funções seta (Arrow Functions)
Funções seta não possuem this próprio. Elas herdam o this do escopo léxico mais próximo:
function criarContador() {
var count = 0;
return {
incrementar: () => {
count++;
console.log(this.valor, count);
}
};
}
var ctx = { valor: "Contador:" };
var contador = criarContador.call(ctx);
contador.incrementar(); // "Contador:" 1
contador.incrementar(); // "Contador:" 2
Diferente das funções seta, funções regulares podem ter seu this alterado:
function criarGetter() {
return function() {
console.log(this.dado);
};
}
var obj1 = { dado: "alpha" };
var obj2 = { dado: "beta" };
var getter = criarGetter.call(obj1);
getter.call(obj2); // "beta" — this pode ser alterado em funções regulares
Em calllbacks assíncronos, funções seta mantêm o this do escopo pai:
function carregar() {
setTimeout(() => {
console.log(this.recurso);
}, 100);
}
var ctx = { recurso: "dados.json" };
carregar.call(ctx); // "dados.json"
Comportamento avançado com objetos aninhados
Funções regulares apontam this para quem as invoca. Funções seta herdam do escopo pai, ignorando o objeto que as contém:
var globalX = 50;
var container = {
globalX: 100,
sub: {
globalX: 200,
metodo: function() {
console.log(this); // sub
var interno = {
globalX: 300,
log: () => {
console.log(this.globalX); // 200 — herda de metodo()
}
};
interno.log();
}
}
};
container.sub.metodo();
Quando o método aninhado é uma seta, o this é herdado do escopo de execução da função pai (não do objeto literal):
var globalX = 50;
var container = {
globalX: 100,
sub: {
globalX: 200,
metodo: () => {
console.log(this); // objeto global — sub não é escopo de execução
var interno = {
globalX: 300,
log: () => {
console.log(this.globalX); // 50 — herda do escopo global
}
};
interno.log();
}
}
};
container.sub.metodo();
Com funções regulares em ambos os níveis, o this de cada uma é determinado por quem a invoca:
var container = {
valor: "raiz",
sub: {
valor: "meio",
metodo: function() {
console.log(this.valor); // "meio"
var interno = {
valor: "fundo",
log: function() {
console.log(this.valor); // "fundo"
}
};
interno.log();
}
}
};
container.sub.metodo();