La palabra clave this en JavaScript es conocida por ser una fuente común de confusión. Su naturaleza de enlace dinámico, donde su valor se detemrina en tiempo de ejecución en lugar de en tiempo de compilación, puede llevar a comportamientos inesperados. Sin embargo, al comprender sus reglas de vinculación, se pueden evitar muchos de estos escollos.
Contextos de this
Analicemos los diferentes escenarios donde this puede apuntar a distintos valores:
1. En el ámbito global
Cuando se utiliza this en el nivel superior de un script (fuera de cualquier función), apunta al objeto global. En navegadores web, este objeto es window.
alert(this); // En un navegador, esto mostrará el objeto window
2. Como una llamada de función simple
Si una función se invoca directamente como una función, el valor de this dentro de ella dependerá del modo de ejecución:
- En modo no estricto,
thisapunta al objeto global (window). - En modo estricto (
'use strict';),thisesundefined.
function mostrarValor(valor) {
this.valorPropio = valor; // En modo no estricto, esto crea una propiedad global
}
mostrarValor(10);
// En modo no estricto: console.log(window.valorPropio); // 10
// En modo estricto: console.log(valorPropio); // undefined (y se lanzaría un error si se intenta acceder a this.valorPropio)
3. Como un método de objeto
Cuando una función se llama como un método de un objeto, this se vincula al objeto al que pertenece el método.
const miObjeto = {
nombre: "EjemploObjeto",
saludar: function(mensaje) {
console.log(`${this.nombre} dice: ${mensaje}`);
}
};
miObjeto.saludar("¡Hola Mundo!"); // Salida: EjemploObjeto dice: ¡Hola Mundo!
4. Como un constructor
Al invocar una función con el operador new, se crea una nueva instancia de objeto. Dentro del constructor, this se refiere a esta nueva instancia que se está creando.
function CreadorDeInstancia(propiedad) {
this.propiedadInstancia = propiedad;
}
const instancia1 = new CreadorDeInstancia("valor1");
// instancia1.propiedadInstancia contendrá "valor1"
5. Funciones anidadas (Callbacks)
Este es uno de los puntos más confusos. Dentro de una función anidada o un callback, this no hereda automáticamente el valor de this de la función externa. Por defecto, apunta al objeto global (o undefined en modo estricto), a menos que se modifique explícitamente.
const persona = {
nombre: "Persona Externa",
actividades: function() {
const decirAlgo = function(accion) {
// 'this' aquí apunta al objeto global (window) o es undefined en modo estricto
console.log(`${this.nombre} está realizando: ${accion}`);
};
decirAlgo("una tarea"); // Salida: undefined está realizando: una tarea (o "Persona Externa está realizando: una tarea" si no está en modo estricto y hay una variable global 'nombre')
}
};
// persona.actividades();
Para solucionar esto, es una práctica común capturar el valor de this de la función externa en una variable local (a menudo llamada that o self) antes de definir la función anidada.
const personaCorregida = {
nombre: "Persona Corregida",
actividades: function() {
const that = this; // Captura el 'this' de personaCorregida
const decirAlgo = function(accion) {
console.log(`${that.nombre} está realizando: ${accion}`);
};
decirAlgo("otra tarea"); // Salida: Persona Corregida está realizando: otra tarea
}
};
// personaCorregida.actividades();
6. Usando call() y apply()
Estos métodos permiten invocar una función especificando explícitamente el valor de this y pasando argumentos.
call(thisArg, arg1, arg2, ...): El primer argumento es el valor quethistomará, seguido por los argumentos individuales de la función.apply(thisArg, [arg1, arg2, ...]): Similar acall, pero los argumentos se pasan como un array.
function presentar(lugar, hora) {
console.log(`${this.nombre} está en ${lugar} a las ${hora}.`);
}
const individuo = { nombre: "Ana" };
presentar.call(individuo, "la oficina", "9 AM"); // Salida: Ana está en la oficina a las 9 AM.
presentar.apply(individuo, ["el parque", "3 PM"]); // Salida: Ana está en el parque a las 3 PM.
Resumen de las reglas de this
En resumen, el valor de this se determina por cómo se llama la función:
- Método de objeto:
thisse refiere al objeto. - Llamada de función simple:
thisse refiere al objeto global (window) oundefineden modo estricto. - Constructor:
thisse refiere a la nueva instancia creada. - Funciones anidadas:
thisno se hereda. Es necesario capturarlo explícitamente si se necesita el valor de un contexto externo.
Una forma simplificada de verlo: si una función usa this, solo se vinculará a un objeto específico si la función es llamada directamente como una propiedad de ese objeto, o si se usa call, apply, o bind para establecer explícitamente el contexto.
El método bind()
En escenarios donde se utilizan callbacks, como en manejadores de eventos, es común necesitar que this se refiera a un objeto específico en lugar del elemento del DOM o el objeto global. El método bind(), introducido en ECMAScript 5, es ideal para esto.
// Ejemplo: Manejador de eventos
const miManejador = {
mensaje: "Clic detectado",
manejarClic: function() {
console.log(this.mensaje);
}
};
// Si simplemente asignamos, 'this' dentro de manejarClic será incorrecto en el callback
// $('#miBoton').on('click', miManejador.manejarClic); // 'this' sería el botón o window
// Usando bind para asegurar el contexto correcto
$('#miBoton').on('click', miManejador.manejarClic.bind(miManejador));
// Al hacer clic, se mostrará: Clic detectado
bind(thisArg, arg1, arg2, ...) crea una nueva función donde el primer argumento (thisArg) se establece permanentemente como el valor de this. Argumentos adicionales proporcionados a bind se prefijan a los argumentos que se pasan a la función cuando se invoca.`
El funcionamiento interno de bind se puede simular de la siguiente manera:
if (!Function.prototype.bind) {
Function.prototype.bind = function(contexto) {
const funcOriginal = this;
const argumentosBinding = Array.prototype.slice.call(arguments, 1);
return function() {
const argumentosLlamada = Array.prototype.slice.call(arguments);
const argumentosFinales = argumentosBinding.concat(argumentosLlamada);
return funcOriginal.apply(contexto, argumentosFinales);
};
};
}
Dominar estos conceptos te permitirá manejar this de manera confiable en tus aplicaciones JavaScript.