El temido "this" en JavaScript, de una vez por todas

Detailed view of programming code in a dark theme on a computer screen.
Foto de Stanislav Kondratiev en Pexels

Si alguna vez te has quedado mirando una función y te has preguntado por qué this no apunta al objeto que esperabas, no estás solo. El comportamiento de this en JavaScript es una de esas trampas que aparecen cuando pasamos de la teoría a la práctica. En este artículo vamos a desmitificarlo, paso a paso, y a ofrecerte una hoja de ruta para que nunca más te sorprenda.

¿Qué es this y por qué nos confunde?

En esencia, this es una referencia al contexto de ejecución de una función. A diferencia de otros lenguajes, donde this siempre apunta al objeto que contiene el método, en JavaScript su valor depende de cómo se llama a la función, no de dónde está declarada. Esa sutileza es la que genera la mayor parte de los errores.

Reglas de enlace de this

  • Modo por defecto (non‑strict): si llamas a una función sin un objeto explícito, this será el objeto global (window en el navegador).
  • Modo estricto: con 'use strict', this será undefined en llamadas directas.
  • Objetos implícitos: cuando usas la sintaxis objeto.metodo(), this apunta a objeto.
  • Constructor (new): al crear una instancia con new, this es el nuevo objeto que se está construyendo.
  • Funciones flecha: las arrow functions no tienen su propio this; capturan el valor del entorno léxico donde se definieron.
  • Explicit binding: los métodos call, apply y bind permiten fijar this de forma manual.

Ejemplo práctico paso a paso

Veamos un caso real que suele generar dudas: un objeto con un método que llama a otra función interna.

const usuario = {
  nombre: 'Álvaro',
  saludo() {
    console.log(Hola, soy ${this.nombre});
    // Llamada a una función externa
    mostrarEdad();
  }
};

function mostrarEdad() {
  // ¿Qué vale this aquí?
  console.log(this.edad);
}

usuario.saludo(); // ¿Qué se muestra?

En el ejemplo anterior, this dentro de saludo apunta a usuario, pero mostrarEdad se ejecuta como una función regular, por lo que this será undefined (modo estricto) o window (modo no estricto). Para que mostrarEdad conozca el contexto correcto, podemos:

  • Pasar explícitamente el objeto: mostrarEdad.call(this);
  • Convertir mostrarEdad en una arrow function, que hereda this de saludo.
function mostrarEdad() {
  console.log(this.edad);
}

const usuario = {
  nombre: 'Álvaro',
  edad: 30,
  saludo() {
    console.log(Hola, soy ${this.nombre});
    mostrarEdad.call(this); // ahora funciona
  }
};

usuario.saludo(); // Hola, soy Álvaro  // 30

Otra alternativa es definir mostrarEdad como una arrow function dentro del objeto:

const usuario = {
  nombre: 'Álvaro',
  edad: 30,
  saludo() {
    console.log(Hola, soy ${this.nombre});
    const mostrarEdad = () => console.log(this.edad);
    mostrarEdad(); // hereda this de saludo
  }
};

usuario.saludo();

Buenas prácticas y trucos para domar this

  • Prefiere funciones flecha en callbacks: cuando el callback necesita acceder al mismo this del contenedor, una arrow function evita sorpresas.
  • Usa bind en eventos: al pasar un método como manejador de eventos, enlázalo con this del componente o clase: this.handleClick = this.handleClick.bind(this);
  • Evita mezclar estilos: no combines funciones tradicionales y flechas en la misma lógica a menos que sepas exactamente qué valor tendrá this.
  • Considera clases ES6: dentro de métodos de clase, this se comporta como en objetos tradicionales, pero los métodos que declares como arrow (por ejemplo en el constructor) capturan el contexto de la instancia.

Conclusión

El “temido” this deja de ser un misterio cuando recordamos que su valor se determina en el momento de la llamada, no en la declaración. Aplicar las reglas que hemos repasado, combinar funciones tradicionales y flechas de forma consciente, y aprovechar call/apply/bind cuando necesitemos forzar un contexto, te permitirá escribir código más predecible y libre de bugs relacionados con el alcance.

La próxima vez que veas un this inesperado, revisa primero cómo se está invocando la función y, si es necesario, reescribe el fragmento con una arrow function o con bind. Con práctica, ese “temido” se convertirá en tu aliado más fiable.