> Manuales > Taller de Javascript

La variable this en Javascript difiere un poco con respecto a otros lenguajes, en este artículo analizamos this y sus variantes en el contexto donde se use.

En el hangout que realizamos, dedicado a las "buenas prácticas en el lenguaje Javascript" vimos varios puntos interesantes que ahora estamos transcribiendo en artículos. Éste es el segundo que publicamos, siendo que el primero se dedicó a tratar la sobrecarga de funciones Nos lo ofreció nuestro compañero Eduard Tomàs, que siempre nos obsequia con grandes joyas de conocimiento.

En los lenguajes de programación más "tradicionales", la variable "this" siempre tiene un valor clarísimo. Sin lugar a dudas, "this" siempre contiene la referencia al objeto sobre el que se está ejecutando un método. Todos los métodos se ejecutan en el contexto de un objeto (esto en términos de programación orientada a objetos) y "this" apunta a este objeto. Sin embargo, en Javascript no es así.

En Javascript, buscando por muchos sitios realmente se encuentran definiciones de "this" un poco complicadas de entender. Te dicen que es "el propietario de la función"... y en caso que no exista propietario, te dicen que "this" es el "objeto global".

Nota: Cuando ejecutas Javascript dentro de una página web, o sea, en un navegador, el objeto global es el objeto "window", a partir del que cuelgan todos los objetos de la jerarquía. Sin embargo, en NodeJS que se ejecuta en una máquina como un lenguaje de programación de propósito general, el objeto global es otra cosa. También existe un objeto global en NodeJS, que tiene una serie de funciones, que hace el rol del objeto "window". Obviamente, en ese objeto global de NodeJS tengo cosas muy distintas de las que encuentras en "window" en un navegador, pero también existe un "contexto global".

Dicho con otras palabras, que podemos encontrar en literatura Javascript, "this es el contexto de una función". Una de las cosas interesantes de Javascript y muy potentes es que el contexto se puede modificar y depende de como se invoca esa función. Repetimos: el valor de "this" dentro de una función no depende de cómo se define esa función, sino de cómo se invoca.

Si yo coloco código fuera de cualquier función y trato de ver el contenido de this, encontraré el contexto global. Puedes probarlo con una línea como esta:

console.log(this);
Si estás en un navegador encontrarás que te manda a la consola el contenido del objeto
"window".

Propietario de la función

Si ahora escribes el código de una función y dentro accedes a "this", lo que encontrarás es el "propietario de la función". Pero esto del "propietario" es un poco complicado de entender, porque básicamente pueden ocurrir dos cosas:
  1. Si la función se ejecuta como global, "this" será el propio objeto global.
  2. Si la función se ejecuta como método de un objeto, entonces "this" es el objeto que está recibiendo este método.

Pero luego existen mecanismos para cambiarlo, como veremos también.

Error típico en Javascript

Ahora veamos un código que podemos encontrarnos por ahí que podría resultar en otros lenguajes correcto, pero que en Javascript denota que el programador no entiende bien que "this" puede apuntar a varias cosas.

//esto estaría conceptualmente mal
var obj = {
nombre: "Edu",
apellidos: "Tomas",
completo: this.nombre + " " + this.apellidos
}
console.log(obj);
//nos mostrará en consola
//Object { nombre="Edu", apellidos="Tomas", completo="undefined undefined"}

Como ves, hemos definido un objeto con la "notación de objeto Javascript" conocida como JSON habitualmente. La propiedad completo, a la que aparentemente se habría asignado el nombre concatenado con los apellidos, en realidad nos queda como "undefined undefined". Esto es porque en ese lugar, "this" es el objeto global, ya que no se ha ejecutado dentro de una función como método de un objeto.

Nota: Te devuelve undefinded porque en el contexto global (objeto "window" teniendo en cuenta que eso se ejecutase en un navegador) "window.nombre" no existe y tampoco "window.apellidos" y si accedemos a una propiedad inexistente de un objeto, Javascript nos devuelve el valor "undefined".

Eso se podría haber "arreglado" convirtiendo a "completo" en una función que devuelve esa concatenación:

var obj = {
nombre: "Edu",
apellidos: "Tomas",
completo: function(){
return this.nombre + " " + this.apellidos;
}
}
console.log(obj.completo());
//nos mostrará en consola "Edu Tomas"

Luego si invocamos a la función completo como método del objeto, "this" sí que será la instancia del objeto sobre el que se llamó al método.

Funciones call / apply / bind

Para terminar de liarlo, vienen tres funciones "muy divertidas" que son "call()", "apply()" y "bind()". Estas funciones las veremos con detalle más adelante, cuando veamos otros aspectos de Javascript, pero básicamente, "call()" y "apply()" sirven para invocar una función pero en la que se suministra un valor de "this" que sustituirá al valor que hubiera tomado en otra situación.

Dicho de otra manera, en la ejecución de una función, el valor de "this" no será el que tocaría, sino el que será el valor que tú le indiques en la llamada a "call()" o "apply()".

Por su parte, "bind()" es un método muy curioso que para empezar, no devuelve un valor, sino que devuelve otra función. Cuando invoco esta otra función que devuelve, el valor de "this" dentro de ella, es el valor original que yo había pasado al invocar a "bind()".

Nota: Tener siempre en cuenta que Javascript es un lenguaje tipo "funcional" (por decirlo de alguna manera), en el que las funciones son ciudadanos de primer orden. El tipo de datos "función" es tan valido como puede ser "string", "object", "number"... Esi significa que puedo pasar funciones como parámetro y puedo devolver funciones como valores de retorno.

Esto es difícil verlo sin un pedazo de código y aunque más adelante queremos ver "bind()" con mayor detalle, creo que estaría bien echar un vistazo:

function bindeando(){
console.log(this);
}
bindeando.bind("hola!!")();

Conclusiones

Más adelante veremos más detalles de "this" y las funciones "call()", "apply()" y "bind()", pero recuerda que este material lo tienes en el evento en vivo "#jsIO Buenas prácticas en el lenguaje Javascript" y en este artículo nos hemos quedado en el minuto 27 de esa presentación.

Eduard Tomàs

Apasionado de la informática, los videojuegos, rol y... la cerveza. Key Consulta...

Manual