Comportamento do this em funções


O valor de this em uma função JavaScript depende do contexto em que a função é chamada. Em alguns casos, esse comportamento pode levar a problemas, especialmente ao usar funções como métodos de objetos.

Problema

No exemplo abaixo, a palavra-chave this não faz referência ao objeto Calculadora, mas sim ao objeto Document, uma vez que o método addEventListener pertence a esse objeto.

function criarCalculadora() {
  return {
    display: document.querySelector(".display"),
 
    iniciar() {
      this.cliqueBotoes();
    },
 
    cliqueBotoes() {
      document.addEventListener("click", function (e) {
        const el = e.target;
 
        console.log(this); // document
 
        if (el.classList.contains("btn-num")) {
          this.btnParaDisplay(el.innerText);
        }
      });
    },
 
    btnParaDisplay(valor) {
      this.display.value += valor;
    },
  };
}
 
const calculadora = criarCalculadora();
calculadora.iniciar();

Solução

Existem duas maneiras comuns de resolver o problema de referência this:

Utilizar a Função bind()

Isso sobrescreve a referência this da função para o objeto Calculadora.

function criarCalculadora() {
 return {
   display: document.querySelector(".display"),
 
   iniciar() {
	 this.cliqueBotoes();
   },
 
   cliqueBotoes() {
	 document.addEventListener(
	   "click",
	   function (e) {
		 const el = e.target;
 
		 console.log(this);
 
		 if (el.classList.contains("btn-num")) {
		   this.btnParaDisplay(el.innerText);
		 }
	   }.bind(this)
	 );
   },
 
   btnParaDisplay(valor) {
	 this.display.value += valor;
   },
 };
}
 
const calculadora = criarCalculadora();
calculadora.iniciar();

Utilizar Arrow Function

Uma arrow function não possui seu próprio this, portanto, ela herda o valor de this do contexto superior, ou seja, referencia o objeto no qual foi definida.

function criarCalculadora() {
  return {
    display: document.querySelector(".display"),
 
    iniciar() {
      this.cliqueBotoes();
    },
 
    cliqueBotoes() {
      document.addEventListener("click", (e) => {
        const el = e.target;
 
        console.log(this);
 
        if (el.classList.contains("btn-num")) {
          this.btnParaDisplay(el.innerText);
        }
      });
    },
 
    btnParaDisplay(valor) {
      this.display.value += valor;
    },
  };
}
 
const calculadora = criarCalculadora();
calculadora.iniciar();

Ambas as abordagens têm seus casos de uso, e a escolha entre elas depende do contexto específico em que você está trabalhando.

Referências