this depende de CÓMO se llama
La regla más importante de this: no importa dónde definiste la función,
importa cómo la invocas. Piensa en this como un asiento vacío que se rellena
en el momento de la llamada, según lo que haya "a la izquierda del punto".
const persona = {
nombre: "Ana",
saludar() {
return "Hola, soy " + this.nombre;
},
};
persona.saludar(); // this = persona -> "Hola, soy Ana"
const suelta = persona.saludar;
suelta(); // this = undefined -> ¡error o "undefined"!
Al guardar el método en suelta y llamarlo "a secas", ya no hay nada a la
izquierda del punto: this se pierde. Esto es la temida "pérdida de this".
Dónde te muerde en la práctica
Ocurre siempre que pasas un método como callback y otro código lo llama por ti:
setTimeout(persona.saludar, 100); // this se pierde
boton.addEventListener("click", obj.manejar); // this se pierde
const { saludar } = persona; // desestructurar también lo desliga
bind: fija el this y devuelve una función nueva
bind no llama a la función: crea una copia atada a un this fijo, que ya
nunca cambia, aunque la invoques suelta.
const saludarAna = persona.saludar.bind(persona);
saludarAna(); // "Hola, soy Ana" (aunque se llame suelta)
setTimeout(saludarAna, 100); // ahora sí funciona
call y apply: invocan AHORA fijando el this
A diferencia de bind, estos ejecutan la función de inmediato indicando qué
this usar. La única diferencia entre ambos es cómo pasas los argumentos:
call: argumentos sueltos, separados por comas.apply: argumentos dentro de un array.
function presentar(saludo, signo) {
return saludo + ", soy " + this.nombre + signo;
}
const ana = { nombre: "Ana" };
presentar.call(ana, "Hola", "!"); // call: args sueltos
presentar.apply(ana, ["Hola", "!"]); // apply: args en array
// Ambos -> "Hola, soy Ana!"
Truco para recordar: apply usa array.
Solución moderna: campos de clase con arrow function
Las funciones flecha no tienen su propio this: lo heredan de donde se
crearon. Si defines un método como campo con arrow, queda "soldado" a la
instancia y nunca pierde el this, aunque lo pases como callback:
class Boton {
constructor(texto) {
this.texto = texto;
}
// campo de clase con arrow: this siempre será esta instancia
clic = () => {
return "Pulsado: " + this.texto;
};
}
const b = new Boton("Aceptar");
const fn = b.clic;
fn(); // "Pulsado: Aceptar" (no se pierde el this)
Ejemplos
La pérdida de this y cómo bind la arregla
const contador = {
valor: 10,
leer() {
return this.valor;
},
};
const suelta = contador.leer;
const atada = contador.leer.bind(contador);
console.log("Atada:", atada()); // 10
console.log("Suelta this:", suelta === contador.leer);
call vs apply: misma función, distinta forma de pasar args
function rango(min, max) {
return this.nombre + " entre " + min + " y " + max;
}
const sensor = { nombre: "Temperatura" };
console.log(rango.call(sensor, 0, 100));
console.log(rango.apply(sensor, [0, 100]));
Arrow en campo de clase: this soldado a la instancia
class Timer {
segundos = 0;
tick = () => {
this.segundos++;
return this.segundos;
};
}
const t = new Timer();
const fn = t.tick; // lo "desligamos"
console.log(fn()); // 1 (sigue funcionando)
console.log(fn()); // 2