Banner keyboard

Andrés D'Amelio

Desarrollador web Front-end

Título

Ingeniero en informática

Ubicación

Venezuela

¿Qué es this en javascript?

Publicado el 17 de mayo de 2021

Banner this javascript

This en javascript es uno de los conceptos más complicados de entender, tiene un comportamiento diferente al this de otros lenguajes de programación como PHP, Java, etc. El valor de this es una referencia a un objeto, esta referencia puede variar dependiendo del contexto donde se esta ejecutando el código. This se comporta de forma diferente dependiendo del modo de ejecución donde se encuentra javascript, estos modos son estricto y no estricto (sloppy mode). Si quieres saber más del modo estricto te dejo un enlace donde puedes conseguir más información modo estricto.

Contexto : Objeto que esta ejecutando la función en un momento especifico.


El proceso donde this toma un valor, se le conoce como enlazamiento (binding en ingles). En este articulo te explicare los distintos tipos de enlazamientos y te daré algunos ejemplos

  • Lexical Binding (Funciones de flecha)
  • New Binding (Instanciar Objetos)
  • Explicit Binding (Invocación indirecta)
  • Implicit Binding (Invocación de método)
  • Default Binding (Invocación directa)

Javascript evalúa ciertas condiciones para determinar que tipo de enlazamiento se debe usar, para esto toma en cuenta ciertas condiciones como

  • El lugar donde es creada la función, es decir, si fue creada dentro de una clase, un objeto, una función global, etc.
  • Modificaciones que ha recibido la función.
  • El lugar donde es invocada.

Ahora veamos a fondo cada uno de estos enlazamientos, partiendo desde el más simple.

Default Binding

Este es el tipo de enlazamiento que viene por defecto en Javascript, es la última opción a tomar en cuenta por el lenguaje, es decir, primero evalúa si el tipo de enlazamiento es cualquiera de los otros cuatro antes mencionados, en caso contrario toma este. El valor que toma this es el objeto global, en el caso de los navegadores web se toma el objeto global es window, por otro lado, cuando trabajamos del lado del servidor con NodeJS es global.

function fn() {
  return this
}
fn() === window 
// Salida: True

Como podemos ver, en este caso this y window apuntan al mismo espacio en memoria, esto lo podemos comprobar

this.saludo = "Hola a todos"
console.log(window.saludo) 
// Salida: Hola a todos


Este tipo de enlazamiento tiene un comportamiento distinto en las funciones cuando ejecutamos el modo estricto en javascript, el valor que toma this es undefined, esto debido a que este modo de ejecución no permite el enlace predeterminado. Fuera de funciones, this sigue siendo el objeto global window.

Implicit Binding

Este enlazamiento ocurre cuando invocamos un método de un objeto. En esta invocación, this toma como valor el objeto que llama dicho método, en otras palabras, toma el valor del objeto que esta a la izquierda del llamado, o lo que esta antes del punto que hace la llamada, veamos un ejemplo:

let persona = {
  nombre: "Bill",
  saludar: function() {
    console.log(`Hola, me llamo ${this.nombre}`)
  },
  esposa: {
    nombre: "Melinda",
    saludar: function() {
      console.log(`Hola, soy ${this.nombre} la esposa de Bill`)
    }
  }
}
persona.saludar()
// Salida: Hola, me llamo Bill
persona.esposa.saludar()
// Salida: Hola, soy Melinda la esposa de Bill

En este ejemplo podemos ver en enlazamiento implícito de una forma más clara, como mencione anteriormente en este tipo, this toma el valor del objeto que invoca al método, en el primer caso, this hace referencia al objeto persona, como se puede observar este objeto es el que invoca al método saludar. Para el segundo caso this toma otro valor, this hace referencia al objeto esposa, con esto se cumple que el valor de this en el enlazamiento implícito hace referencia al objeto que invoca al método.

Explicit Binding

Este enlazamiento no permite mantener o cambiar el valor de this según nuestras necesidades, en javascript es muy común perder el valor de this sin darnos cuenta, como vimos en el ejemplo anterior this no siempre es el mismo y su valor siempre está cambiando, con este enlazamiento podemos atar un valor a this. Este tipo se conoce como enlazamiento fuerte. Tenemos tres métodos que nos permiten aplicar este enlazamiento, estos son call, apply y bind.

Call

Este método nos permite cambiarle el contexto a una función al momento de ejecutarla. Es decir, veamos esto como tomar prestada una función para usarla con otro objeto, y que this tome el valor de ese objeto. Este método recibe como primer parámetro el objeto al que vamos a atar a la función, y otros parámetros opcionales que se pasan uno a uno en caso de que la función los requiera. Veamos un ejemplo

function verTipado(tipado) {
  console.log(`El lenguaje de programación ${this.nombre} es de tipado ${tipado}`)
}

const java = { nombre: "Java" }
const javascript = { nombre: "Javascript" }

verTipado.call(java, "fuerte")
// Salida: El lenguaje de programación Java es de tipado fuerte
verTipado.call(javascript, "débil")
// Salida: El lenguaje de programación Javascript es de tipado débil

Si ejecutamos directamente la función verTipado, el valor de this es window, que es el contexto global, en este punto, esta función no esta relacionada con los objetos java y javascript, si queremos que estén relacionados podemos usar este método, con esto creamos el enlazamiento, de allí el nombre de explicito. En el ejemplo vemos que pasamos el valor del parámetro que recibe la función, esto puede variar según la función, si recibe más de uno se pasan separados por coma. Veamos

funcion.call(obj, arg1, arg2, ... , argn)

apply

Este método funciona de forma similar a call, con una única diferencia y es que los parámetros de la función son enviados en un arreglo, veamos como quedaría con el ejemplo anterior

function verTipado(tipado) {
  console.log(`El lenguaje de programación ${this.nombre} es de tipado ${tipado}`)
}

const java = { nombre: "Java" }
const javascript = { nombre: "Javascript" }

verTipado.apply(java, ["fuerte"])
// Salida: El lenguaje de programación Java es de tipado fuerte
verTipado.apply(javascript, ["débil"])
// Salida: El lenguaje de programación Javascript es de tipado débil

En este caso los parámetros que recibe la función son enviados en un arreglo

funcion.apply(obj, [arg1, arg2, ... , argn])

El resultado al usar call y apply es el mismo, en mi opinión es más sencillo usar apply, usando este método evitamos cambiar la llamada de la función, solo agregamos un nuevo valor al arreglo.


bind

De los ejemplos anteriores, cuando invocábamos a los métodos call y apply obtenemos resultados de forma inmediata, con bind no ocurre eso, este método; que es uno de los más usados, crea una nueva función y la enlaza al objeto que recibe como parámetro. Es muy útil cuando necesitamos ejecutar una función asociada a un objeto múltiples veces. Veamos un ejemplo

function verTipado(tipado) {
  console.log(`El lenguaje de programación ${this.nombre} es de tipado ${tipado}`)
}

const javascript = { nombre: "Javascript" }

// Asignamos la nueva función a una constante
const tipadoJavascript = verTipado.bind(javascript, "débil")

tipadoJavascript()
// Salida: El lenguaje de programación Javascript es de tipado débil


Nota : Las funciones que fueron creadas con bind no pueden volver a ser enlazadas con otro objeto.


Este método sirve mucho cuando trabajamos sobre los métodos de una clase, pues algunos otros métodos modifican el valor de this, uno de estos es setTimeout, dentro de este, this toma el valor del objeto global window, en estos caso podemos usar bind para mantener la referencia del contexto.

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

    saludar() {
        setTimeout(this.presentarse.bind(this), 500)
    }

    presentarse() {
        console.log(`Hola, me llamo ${this.nombre} ${this.apellido}`)
    }
}

const bill = new Persona("Bill", "Gates")
bill.saludar()
// Salida: Hola, me llamo Bill Gates

En este ejemplo, tenemos dos métodos que pertenecen a la clase persona: presentarse, que imprime un mensaje por consola, y saludar, que ejecuta al método presentarse después de 500 milisegundos. Y con el fin de mantener la referencia a la clase persona, pasamos el método presentarse al setTimeout, invocando al bind.


New Binding

Un viejo conocido, cuando construimos clases o funciones constructoras, creamos instancias de ellas, para esto usamos la palabra reservada new, lo que indica que creará una instancia de esa clase o función constructora, en este punto se produce un enlazamiento de this con el objeto creado, ya en los ejemplos anteriores tuvimos un acercamiento a este enlazamiento, veamos un nuevo ejemplo

// Usando función constructora
function Persona(nombre, apellido) {
    this.nombre = nombre
    this.apellido = apellido
}
const tim = new Persona("Tim", "Cook")

// Usando clases
class Persona {
    constructor(nombre, apellido) {
        this.nombre = nombre
        this.apellido = apellido
    }
}

const bill = new Persona("Bill", "Gates")

Al crear cada instancia se produce el enlazamiento con el objeto creado, para el primer caso this tomará al objeto tim, en el segundo caso this tomará el valor de bill. Para este tipo de enlazamiento no podemos usar los métodos bind, call y apply, porque estamos creando nuevas instancias de objetos, y estos son enlazados directamente.


Lexical Binding

Este tipo de enlazamiento se da cuando usamos funciones de flecha. Las funciones de flecha tienen un comportamiento en particular y es que se ejecutan en el mismo contexto donde fueron creadas, por lo que el valor de this dentro de una función de flecha es el objeto que envuelve dicha función. Veamos un caso donde el valor de this no es el deseado

const lenguaje = {
  nombre: "C++",
  verTipado: () => {
    console.log(`El lenguaje de programación ${this.nombre} es fuertemente tipado`)
  }
}
lenguaje.verTipado()
// Salida: El lenguaje de programación undefined es fuertemente tipado

this.nombre es undefined, podríamos pensar que el valor de this esta enlazado con el objeto lenguaje, en este caso no ocurre de esa forma, verTipado se carga en memoria cuando se crea el objeto lenguaje, en este punto de la creación this esta enlazado con el objeto global window. En las funciones de flecha no siempre el valor de this es window, veamos un caso,

const lenguaje = {
  nombre: "C++",
  verTipado: function() {	
    setTimeout(()  => {
      console.log(`El lenguaje de programación ${this.nombre} es fuertemente tipado`)
    }, 1000)
  }
}
lenguaje.verTipado()
// Salida: El lenguaje de programación C++ es fuertemente tipado

Como mencione anteriormente, this dentro de setTimeout es window, como la función a ejecutar es una función de flecha tomará el valor del contexto donde fue creada, en este caso fue creada dentro de la función verTipado, esta función para ese momento esta enlazada al objeto lenguaje, por lo que this dentro del setTimeout es el objeto lenguaje.

Este tipo de enlazamiento es útil cuando queremos que this haga referencia al contexto externo.


Conclusión

Con esto completamos todos los tipos de enlazamiento, y como estos pueden entrar en acción dependiendo de como escribamos nuestro código. Con esto espero que hayas aprendido todo sobre this y puedas usarla de una mejor forma en tus aplicaciones. Si te gustó compártelo en tus redes y así ayudamos a más personas a entender este concepto tan confuso en javascript. Si tienes alguna duda déjala en los comentarios y te responderé en la brevedad posible.