La palabra clave this
de JavaScript es uno de los aspectos más difíciles de comprender del lenguaje. Pero es de vital importancia para escribir código JavaScript más avanzado.
En JavaScript, la palabra clave this
nos permite:
- Reutilizar funciones en diferentes contextos de ejecución. Es decir, una función una vez definida se puede invocar para diferentes objetos usando la palabra clave
this
. - Identificar el objeto en el contexto de ejecución actual cuando invocamos un método.
La palabra clave this
estrechamente asociada con las funciones de JavaScript. Cuando se trata de this
, lo fundamental es entender dónde se invoca una función. Porque no sabemos qué hay en la palabra clave this
hasta que se invoca la función.
El uso de this
se puede clasificar en cinco aspectos vinculantes (binding
) diferentes. En este artículo, aprenderemos sobre los cinco aspectos con ejemplos.
Primero, ¿Qué es la vinculación (binding)?
En JavaScript, un Entorno Léxico
(Lexical Environment) es donde se escribe físicamente tu código. En el siguiente ejemplo, el nombre de la variable está léxicamente
dentro de la función decirNombre()
.
function decirNombre() {
let nombre = 'algunNombre';
console.log('El nombre es, ', nombre);
}
Un Contexto de Ejecución
(Execution Context) se refiere al código que se está ejecutando actualmente y todo lo demás que ayuda a ejecutarlo. Puede haber muchos entornos léxicos disponibles, pero el contexto de ejecución que se está ejecutando actualmente es manejado por el Contexto de Ejecución.
Cada uno de los contextos de ejecución contiene un Registro de Entorno
(Environment Record). A medida que el motor JavaScript ejecuta el código, las variables y los nombres de las funciones se agregan al registro de entorno.
Este fenómeno se conoce como vinculación en JavaScript. La vinculación ayuda a asociar los identificadores (variables, nombres de funciones) con la palabra clave this
para un contexto de ejecución.
No se preocupe si encuentra esto un poco difícil de entender ahora. Obtendrá una mejor comprensión a medida que avancemos.
Regla #1: Cómo funciona la vinculación implícita de JavaScript
La vinculación implícita cubre la mayoría de los casos de uso para tratar con la palabra clave this
.
En la vinculación implícita, debe verificar lo que está a la izquierda del operador de punto (.) adyacente a una función en el momento de la invocación. Esto determina a saber donde la palabra clave this
está vinculada.
Veamos un ejemplo para entenderlo mejor.
let usuario = {
nombre: 'Tapas',
direccion: 'freecodecamp',
getName: function() {
console.log(this.nombre);
}
};
usuario.getName();
Aquí, this
está vinculado al objeto usuario. Sabemos esto porque a la izquierda del operador punto(.) adyacente a la función getName()
, vemos el objeto usuario
. Entonces this.name
va a registrar Tapas en la consola.
Veamos otro ejemplo para comprender mejor este concepto:
function decorateLogName(obj) {
obj.logName = function() {
console.log(this.nombre);
}
};
let tom = {
nombre: 'Tom',
edad: 7
};
let jerry = {
nombre: 'jerry',
edad: 3
};
decorateLogName(tom);
decorateLogName(jerry);
tom.logName();
jerry.logName();
En este ejemplo, tenemos dos objetos, tom
y jerry
. Hemos decorado (mejorado) estos objetos adjuntando un método llamado logName()
.
Observa que cuando invocamos tom.logName()
, el objeto tom
esta a la izquierda del operador punto(.) adyacente a la función logName()
. Entonces this
está vinculado al objeto tom
y registra el valor de tom ( this.nombre
es igual a tom aquí). Lo mismo se aplica cuando se invoca jerry.logName()
.
Regla #2: Cómo funciona la vinculación explícita de JavaScript (explicit binding)
Hemos visto que JavaScript crea un entorno para ejecutar el código que escribimos. Se encarga de la creación de memoria para variables, funciones, objetos, etc. en la fase de creación. Finalmente ejecuta el código en la fase de ejecución. Este entorno especial se denomina Contexto de ejecución
(execution context).
Puede haber muchos entornos de este tipo (contextos de ejecución) en una aplicación JavaScript. Cada contexto de ejecución opera independientemente de los demás.
Pero a veces, es posible que deseemos usar cosas de un contexto de ejecución en otro. Ahí es donde entra en juego la vinculación explícita.
En la vinculación explícita, podemos llamar a una función con un objeto cuando la función está fuera del contexto de ejecución del objeto.
Hay tres métodos muy especiales, call()
, apply()
y bind()
que nos ayudan a lograr una vinculación explícita.
Como funciona el método call()
de JavaScript
Con el método call()
, el contexto con el que se debe llamar a la función se pasará como parámetro call()
. Veamos cómo funciona con un ejemplo:
let getName = function() {
console.log(this.nombre);
}
let usuario = {
nombre: 'Tapas',
direccion: 'freecodecamp'
};
getName.call(usuario);
Aquí, el método call()
se invoca en una función llamada getName()
. La función getName()
simplemente registra this.nombre
. ¿Pero qué es this
aquí? Eso se determina por lo que se ha pasado al método call()
.
Aquí, this
se vinculará al objeto de usuario porque hemos pasado el usuario como parámetro al método call()
. Entonces this.nombre
debe registrar el valor de la propiedad de nombre del objeto de usuario, es decir, Tapas.
En el ejemplo anterior, hemos pasado solo un argumento para call()
. Pero también podemos pasar múltiples argumentos a call()
, así:
let getName = function(pasatiempo1, pasatiempo2) {
console.log(this.nombre + ' le gusta ' + pasatiempo1 + ' , ' + pasatiempo2);
}
let usuario = {
nombre: 'Tapas',
direccion: 'Bangalore'
};
let pasatiempos = ['Nadar', 'Escribir'];
getName.call(usuario, pasatiempos[0], pasatiempos[1]);
Aquí hemos pasado varios argumentos al método call()
. El primer argumento debe ser el contexto del objeto con el que se debe invocar la función. Otros parámetros podrían ser simplemente valores para usar.
Aquí estoy pasando Nadar y Escribir como dos parámetros para la función getName()
.
¿Notaste un punto crítico aquí? En el caso de una call()
, los argumentos deben pasarse uno por uno, lo cual no es una forma inteligente de hacer las cosas. Ahí es donde nuestro método siguiente, apply()
, entra en escena.
Cómo funciona el método apply()
de JavaScript
Esta forma frenética de pasar argumentos al método call()
puede resolverse con otro método alternativo llamado apply()
. Es exactamente lo mismo que call()
pero le permite pasar los argumentos de manera más conveniente. Echar un vistazo:
let getName = function(pasatiempo1, pasatiempo2) {
console.log(this.name + ' le gusta ' + pasatiempo1 + ' , ' + pasatiempo2);
}
let usuario = {
nombre: 'Tapas',
direccion: 'Bangalore'
};
let pasatiempos = ['Nadar', 'Escribir'];
getName.apply(usuario, pasatiempos);
Aquí podemos pasar una serie de argumentos, lo que es mucho más conveniente que pasarlos uno por uno.
Consejo: cuando solo tenga un argumento de valor o ningún argumento de valor para pasar, use call()
. Cuando tenga varios argumentos de valor para pasar, use apply()
.
Cómo funciona el método bind()
de JavaScript
El método bind()
es similar al método call()
pero con una diferencia. A diferencia del método call()
de llamar a la función directamente, bind()
devuelve una función nueva y podemos invocarla en su lugar.
let getName = function(pasatiempo1, pasatiempo2) {
console.log(this.name + ' le gusta ' + pasatiempo1 + ' , ' + pasatiempo2);
}
let usuario = {
nombre: 'Tapas',
direccion: 'Bangalore'
};
let pasatiempos = ['Nadar', 'Escribir'];
let nuevaFn = getName.bind(usuario, pasatiempos[0], pasatiempos[1]);
nuevaFn();
Aquí, getName.bind()
no invoca la función getName()
directamente. Devuelve una nueva función, newFn
y podemos invocarla como newFn()
.
Regla #3: La vinculación new
de JavaScript
La palabra clave new
se utiliza para crear un objeto a partir de la función constructora.
let Caricatura = function(nombre, personaje) {
this.nombre = nombre;
this.personaje = personaje;
this.log = function() {
console.log(this.nombre + ' is a ' + this.personaje);
}
};
Puede crear objetos usando la palabra clave new
como esta:
let tom = new Caricatura('Tom', 'Gato');
let jerry = new Caricatura('Jerry', 'Raton');
Cuando se invoca una función con la palabra clave new
, JavaScript un objeto interno this
(como, this = {}) dentro de la función. El recién creado this
se una al objeto que se está creando utilizando la palabra clave new
.
¿Suena complejo? Ok, analicémoslo. Toma esta línea,
let tom = new Caricatura('Tom', 'Gato');
Aquí se invoca la función Caricatura con la palabra clave new
. Entonces, el this
creado internamente estará vinculado al nuevo objeto que se está creando aquí, el cual es tom.
Regla #4: Vinculación de Objetos Globales de JavaScript
¿Cuál crees que será el resultado del siguiente código? ¿A qué se vincula this
aquí?
let sayName = function(nombre) {
console.log(this.nombre);
};
window.nombre = 'Tapas';
sayName();
Si la palabra clave this
no se resuelve con ninguno de las vinculaciones, implicit
(implícito), explicit
(explícito) o new
, la palabra clave this
se enlaza con el objeto window(global)
.
Sin embargo, hay une excepción. El modo estricto (strict mode) de JavaScript no permite esta vinculación predeterminada.
"use strict";
function myFunction() {
return this;
}
En el caso anterior, la palabra clave this
no está definida (undefined.
)
Regla #5: Vinculación de elemento de evento HTML en JavaScript (event element)
En los controladores de eventos de HTML, la palabra clave this
se vincula a los elementos HTML que reciben el evento.
<button onclick="console.log(this)">¡Haz clic aquí!</button>
El es el registro de salida (output log) en la consola cuando hace clic en el botón:
"<button onclick='console.log(this)'>¡Haz clic aquí!</button>"
Puedes cambiar el estilo del botón usando la palabra clave this
, así:
<button onclick="this.style.color='teal'">¡Haz clic aquí!</button>
Pero tenga cuidado cuando llame a una función en el botón clic y use la palabra clave this
dentro de esa función.
<button onclick="cambiarColor()">¡Haz clic aquí!</button>
y en JavaScript:
function cambiarColor() {
this.style.color='teal';
}
El código anterior no funcionará como se esperaba. Como hemos visto en la Regla 4, aquí esto estará ligado al objeto global (en el modo 'no estricto') donde no hay un objeto de estilo para establecer el color.
En Resumen
Para resumir,
- En el caso de la vinculación implícita, la palabra clave
this
se vincula a la izquierda del operador punto (.). - En el caso de una vinculación explícita, podemos llamar a una función con un objeto cuando la función está fuera del contexto de ejecución del objeto. Los métodos
call()
,apply()
, andbind()
juegan un papel importante aquí. - Cuando se invoca una función con la palabra clave
new
, la palabra clavethis
dentro de la función se une al nuevo objeto que se está construyendo. - Cuando la palabra clave
this
no se resuelve con ninguno de las vinculaciones,implicit
(implícito),explicit
(explícito) onew
, se enlaza con el objetowindow(global)
. En el modo estricto (strict mode) de JavaScript, la palabra clavethis
no estará definida (undefined). - En los controladores de eventos HTML, la palabra clave
this
se vincula a los elementos HTML que reciben el evento.
Hay un caso más en el que la palabra clave this
se comporta de manera diferente, como con las funciones de flecha de ES6
(ES6 arrow functions). Veremos eso en un artículo futuro.
Espero que este artículo le haya resultado útil.
Si este artículo fue útil, compártelo para que otros también puedan leerlo. Puedes @ me on Twitter (@tapasadhikary) con comentarios, o no dudes en seguirme.
Traducido del artículo de Tapas Adhikary - The JavaScript `this` Keyword + 5 Key Binding Rules Explained for JS Beginners