DevPath · Aprende a programar ESPTEN

Programación orientada a objetos

this dinámico: bind, call y apply

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:

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
Pon esto en práctica

DevPath es un curso práctico: aquí lees la teoría; en la app la pones en práctica con ejercicios que se ejecutan de verdad, sin conexión.

Empezar gratis en la app →
← Herencia y encapsulaciónMiembros estáticos y métodos privados →