Entendendo a palavra-chave this no JavaScript

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();

Tags: javascript this-keyword arrow-functions bind call

Publicado em 6-25 18:20