Hamburger Icon
El contexto de 'this' en JavaScript

El contexto de 'this' en JavaScript

En JavaScript, el uso de la palabra clave this puede resultar confuso al principio, pero bastan unos pocos ejemplos para verlo mucho más claro.

JavaScript es un lenguaje muy versátil y, entre otras formas, se puede usar como un lenguaje orientado a objetos. Esto significa que en JavaScript existen clases y objetos.

Sería bueno que tuvieras unas nociones básicas sobre funciones en JavaScript, puedes hacer un breve repaso en este post para entender las funciones desde cero.

Para qué quiero saber esto

Si has llegado hasta este post, algún motivo debes de tener, pero te voy a dar alguno más.

En primer lugar, para entender tu lenguaje favorito. En segundo lugar, para poder leer el código fuente de muchos frameworks o librerías, ya que es habitual el uso y manipulación del this.

En tercer lugar, al trabajar con eventos en JavaScript, this se utiliza a menudo para hacer referencia al elemento en el que se desencadena el evento.

Si esto te parece poco... no hace falta que sigas leyendo. En caso contrario, adelante, como si estuvieras en tu casa.

Básicos de this

La palabra this se refiere al objeto actual en el que se está ejecutando el código. Su valor depende de cómo se llama y dónde se utiliza.

Si ejecutas este código, ¿qué crees que verás por consola?

console.log(this)

Si estás en el ámbito global, es decir, no has ejecutado esa línea dentro de ninguna clase u objeto, entonces verás por consola el objeto window del navegador.

Por lo tanto, la primero que tiene que quedar muy claro, es que this se refiere al objeto actual donde se ejecuta la línea de código (a no ser que alteres este comportamiento intencionadamente).

Prueba algo así:

const persona = {
  nombre: "Pol",
  saludar: function () {
    console.log("Hola, mi nombre es " + this.nombre)
  },
}

persona.saludar() // Muestra "Hola, mi nombre es Pol"

La función saludar está dentro del objeto persona, por lo tanto a través de this podremos acceder a las propiedades y atributos de este objeto.

En cambio, si llamas a una función que no se encuentre -aparentemente- dentro de un objeto, sucede algo diferente:

function saludar() {
  console.log("Hola, mi nombre es " + this.nombre)
}

saludar() // Muestra "Hola, mi nombre es undefined"

En este caso la función saludar no está dentro de un objeto, por lo tanto nombre no está definido. Para ser exactos, this está haciendo referencia al objeto global window, que por defecto no tiene ningún atributo nombre.

Si esto queda claro, ya tienes mucho ganado, pero hay más.

Métodos call, apply y bind

JavaScript proporciona métodos como call, apply y bind para establecer explícitamente el valor de this en una función. Estos métodos son útiles cuando se desea especificar el objeto al que se debe hacer referencia con this. Por ejemplo:

const persona1 = {
  nombre: "Juan",
}

const persona2 = {
  nombre: "María",
}

function saludar() {
  console.log("Hola, mi nombre es " + this.nombre)
}

saludar.call(persona1) // Muestra "Hola, mi nombre es Juan"
saludar.call(persona2) // Muestra "Hola, mi nombre es María"

Estamos usando el método call para invocar la función saludar con un objeto específico cada vez, por lo que this se refiere en cada momento al objeto pertinente. Puedes más info de call en MDN.

Vamos a ver ahora un ejemplo usando el método apply, que permite llamar a una función con un objeto especificado como el valor de this, junto con un array -o un objeto similar a un array- que contiene los argumentos pasados a la función.

Se ve más claro en el ejemplo:

function saludar(mensaje) {
  console.log(mensaje + ", mi nombre es " + this.nombre)
}

const persona = {
  nombre: "Pol",
}

saludar.apply(persona, ["Hola"]) // Muestra "Hola, mi nombre es Pol"

Definimos de forma separada la función saludar y el objeto persona. Luego usamos el método apply para llamar la función saludar indicando que this debe estar asociado a persona, además de un array con el parámetro que necesita la función.

En otras palabras, en el primer argumento le decimos a la función dónde debe apuntar (o qué es) this, en el segundo argumento los parámetros que necesite la función sobre la que se aplica el método apply.

En MDN puedes ver más ejemplos.

Finalmente, toca ver algún ejemplo con bind. Este método crea una nueva función enlazada a un objeto específico como valor de this. Esta nueva función puede ser llamada más tarde:

function saludar(mensaje) {
  console.log(mensaje + ", mi nombre es " + this.nombre)
}

const persona = {
  nombre: "Pol",
}

const saludarPersona = saludar.bind(persona)
saludarPersona("Hola") // Muestra "Hola, mi nombre es Pol"

Como ves, es bastante parecido a apply, pero nos permite separar la parte de indicar dónde apunta this en una función y la llamada a la misma en dos pasos, lo cual puede ser conveniente. Cómo no, aquí tienes la documentación de MDN para que le eches un ojo si te apetece.

Clases JavaScript y this

En una clase el comportamiento de this puede variar dependiendo de cómo se utilice. En el constructor, usaremos this para establecer las propiedades de la clase:

class Persona {
  constructor(nombre) {
    this.nombre = nombre
  }
}

const persona = new Persona("Pol")
console.log(persona.nombre) // Muestra "Pol"

En los métodos que definamos en una clase, this se refiere al objeto en el que se está llamando el método, es decir, a las propiedades de la clase:

class Persona {
  constructor(nombre) {
    this.nombre = nombre
  }

  saludar() {
    console.log("Hola, mi nombre es " + this.nombre)
  }
}

const persona = new Persona("Pol")
persona.saludar() // Muestra "Hola, mi nombre es Pol"

Aquí, this.nombre hace referencia a la propiedad nombre del objeto en el que se llama el método saludar.

Como ves, el comportamiento es el mismo que antes, solo has de tener en cuenta que el objeto donde se llaman los métodos es la propia clase. La clave está en entender el contexto.

Arrow functions y this

Si utilizas arrow functions dentro de una clase, el valor de this está determinado por el contexto léxico en el que se define la arrow function y no por el objeto en el que se llama.

Esto significa que this en una arrow function dentro de una clase hace referencia al objeto o ámbito exterior. Por ejemplo:

lass Persona {
  constructor(nombre) {
    this.nombre = nombre;
  }

  saludarArrow = () => {
    console.log("Hola, mi nombre es " + this.nombre);
  };
}

const persona = new Persona("Pol");
const saludarArrow = persona.saludarArrow;
saludarArrow(); // Muestra "Hola, mi nombre es Pol"

Puedes ver como this.nombre dentro de la arrow function saludarArrow hace referencia al objeto persona, incluso si se llama a la función en un ámbito externo.

Hasta aquí bien, porque parece que el comportamiento de una arrow function es igual que a la de una función normal, pero mira este otro ejemplo para entender mejor esto del contexto léxico:

class Persona {
  constructor(nombre) {
    this.nombre = nombre
  }

  saludar() {
    setTimeout(() => {
      console.log("Hola, mi nombre es " + this.nombre)
    }, 1000)
  }
}

const persona = new Persona("Juan")
persona.saludar() // Después de 1 segundo, muestra "Hola, mi nombre es Juan"

const saludarArrow = persona.saludar
saludarArrow() // Después de 1 segundo, muestra "Hola, mi nombre es undefined"

En este ejemplo, el método saludar de la clase Persona utiliza una arrow function dentro de setTimeout. Dentro de la arrow function, this sigue refiriéndose al objeto persona, ya que su contexto léxico es la clase Persona. Por lo tanto, this.nombre dentro de la arrow function es válido y muestra el nombre correctamente.

Sin embargo, cuando se asigna persona.saludar a saludarArrow y se llama a saludarArrow() directamente, el contexto léxico cambia y this se refiere al objeto global (por ejemplo, window en un navegador o global en Node.js) en lugar del objeto persona. Por lo tanto, this.nombre dentro de la arrow function se resuelve como undefined en ese contexto.

Es importante tener en cuenta que el valor de this en una arrow function se determina en el momento de su creación y no puede ser cambiado mediante los métodos call, apply o bind, a diferencia de las funciones regulares.

Conclusiones

Hemos visto que el valor de this depende de cómo se llama y dónde se utiliza. Luego, hemos revisado cómo adaptar el valor de this a nuestras necesidades con los métodos call, apply y bind. Finalmente, hemos visto ejemplos de this en clases JavaScript y en las arrow functions.

Entender el funcionamiento de this en JavaScript es fundamental para trabajar de manera efectiva con objetos, clases y funciones en el lenguaje.