CAP 09 · LEC 03·Programación orientada a objetos

this y binding: el contexto de ejecución

El valor de this en JavaScript depende de cómo se llama una función, no de dónde está escrita. Entender esto es crucial para evitar errores sutiles en clases y callbacks.

● AVANZADO10 min lectura4 ejerciciospor Fernando Herrera · actualizado mayo de 2026
¿Encontraste un error o algo que mejorar?Editá esta lección en GitHub →

¿Qué es this?

this es una referencia al contexto de ejecución actual — el objeto desde el que se llama una función. Su valor no es fijo: cambia según cómo se invoca la función:

function mostrarNombre() { console.log(this.nombre); } const persona1 = { nombre: "Ana", mostrarNombre }; const persona2 = { nombre: "Carlos", mostrarNombre }; persona1.mostrarNombre(); // "Ana" ← this es persona1 persona2.mostrarNombre(); // "Carlos" ← this es persona2 // Llamada directa (sin contexto) mostrarNombre(); // undefined (en strict mode) o window.nombre en el navegador
function mostrarNombre(this: { nombre: string }) { console.log(this.nombre); } const persona1 = { nombre: "Ana", mostrarNombre }; const persona2 = { nombre: "Carlos", mostrarNombre }; persona1.mostrarNombre(); // "Ana" persona2.mostrarNombre(); // "Carlos"
SalidaAna Carlos
Regla fundamental

this es determinado en el momento de la llamada, no en el momento de la definición. Este es el origen de la mayoría de los bugs relacionados con this.

this en métodos de objeto

Dentro de un método de objeto, this apunta al objeto que contiene el método — siempre que se llame como propiedad del objeto:

class Contador { constructor() { this.valor = 0; } incrementar() { this.valor++; return this.valor; } reiniciar() { this.valor = 0; } } const c = new Contador(); console.log(c.incrementar()); // 1 console.log(c.incrementar()); // 2 console.log(c.incrementar()); // 3 c.reiniciar(); console.log(c.incrementar()); // 1
class Contador { valor: number = 0; incrementar(): number { this.valor++; return this.valor; } reiniciar(): void { this.valor = 0; } } const c = new Contador(); console.log(c.incrementar()); // 1 console.log(c.incrementar()); // 2 console.log(c.incrementar()); // 3 c.reiniciar(); console.log(c.incrementar()); // 1
Salida1 2 3 1

this perdido: callbacks y event handlers

El problema más común: extraer un método de su objeto hace que pierda el contexto:

class Temporizador { constructor() { this.segundos = 0; } iniciar() { // ❌ Problema: la función pasada a setInterval pierde 'this' setInterval(function() { this.segundos++; // TypeError: Cannot read 'segundos' of undefined console.log(this.segundos); }, 1000); } } // Otro caso común: extraer el método class Saludador { constructor(nombre) { this.nombre = nombre; } saludar() { return `Hola, ${this.nombre}`; } } const s = new Saludador("Ana"); const fn = s.saludar; // ← extrae el método sin el contexto console.log(s.saludar()); // "Hola, Ana" ✅ // console.log(fn()); // ❌ this.nombre es undefined
class Saludador { nombre: string; constructor(nombre: string) { this.nombre = nombre; } saludar(): string { return `Hola, ${this.nombre}`; } } const s = new Saludador("Ana"); const fn = s.saludar; // ← extrae el método sin el contexto console.log(s.saludar()); // "Hola, Ana" ✅ // fn(); // ❌ Error en runtime: this es undefined

Soluciones: bind, arrow functions y campos de clase

Existen tres formas de garantizar que this sea el correcto:

class Temporizador { constructor() { this.segundos = 0; // Solución 3: campo de clase como arrow function this.tick = () => { this.segundos++; console.log(`Tick: ${this.segundos}`); }; } // Solución 1: bind en el método iniciarConBind() { const fn = this.tick.bind(this); setTimeout(fn, 1000); } // Solución 2: arrow function como callback iniciarConArrow() { setTimeout(() => { this.segundos++; console.log(`Arrow tick: ${this.segundos}`); }, 1000); } } // Comparación rápida: class Ejemplo { valor = 42; // Método normal — this se puede perder metodoNormal() { return this.valor; } // Campo de clase arrow — this siempre es la instancia metodoArrow = () => this.valor; } const e = new Ejemplo(); const normal = e.metodoNormal; const arrow = e.metodoArrow; // console.log(normal()); // ❌ undefined o error console.log(arrow()); // ✅ 42
class Temporizador { segundos: number = 0; // Solución 1: bind en el constructor constructor() { this.iniciar = this.iniciar.bind(this); } iniciar(): void { this.segundos++; console.log(`Tick: ${this.segundos}`); } // Solución 2: arrow function inline iniciarConArrow(): void { setTimeout(() => { this.segundos++; console.log(`Arrow tick: ${this.segundos}`); }, 100); } } // Solución 3: campo de clase arrow (más moderna) class Boton { etiqueta: string; constructor(etiqueta: string) { this.etiqueta = etiqueta; } // Campo de clase — this siempre es la instancia handleClick = (): void => { console.log(`Clic en: ${this.etiqueta}`); }; } const btn = new Boton("Enviar"); const handler = btn.handleClick; // extrae sin perder this handler(); // ✅ "Clic en: Enviar"
SalidaClic en: Enviar
¿Cuál solución usar?

Para React y event handlers modernos, usa campos de clase arrow (handleClick = () => {}). Para lógica general, las arrow functions inline son más limpias. El bind en el constructor es el enfoque más antiguo y verboso.

call() y apply(): this explícito

call() y apply() permiten llamar una función con un this específico:

function presentar(saludo, puntuacion) { return `${saludo}, soy ${this.nombre}${puntuacion}`; } const persona = { nombre: "Ana" }; // call: argumentos separados por coma const r1 = presentar.call(persona, "Hola", "!"); console.log(r1); // "Hola, soy Ana!" // apply: argumentos como array const r2 = presentar.apply(persona, ["Buenos días", "."]); console.log(r2); // "Buenos días, soy Ana." // Caso práctico: reutilizar un método entre objetos const calcular = { impuesto: 0.16, conImpuesto(precio) { return precio * (1 + this.impuesto); }, }; const calcularMexico = { impuesto: 0.16 }; const calcularEuropa = { impuesto: 0.21 }; console.log(calcular.conImpuesto.call(calcularMexico, 100)); // 116 console.log(calcular.conImpuesto.call(calcularEuropa, 100)); // 121
function presentar( this: { nombre: string }, saludo: string, puntuacion: string ): string { return `${saludo}, soy ${this.nombre}${puntuacion}`; } const persona = { nombre: "Ana" }; const r1 = presentar.call(persona, "Hola", "!"); console.log(r1); // "Hola, soy Ana!" const r2 = presentar.apply(persona, ["Buenos días", "."]); console.log(r2); // "Buenos días, soy Ana." // Diferencia: call vs apply vs bind // call(thisArg, arg1, arg2) — invoca inmediatamente // apply(thisArg, [arg1, arg2]) — invoca inmediatamente con array // bind(thisArg, arg1) — retorna nueva función, NO invoca
SalidaHola, soy Ana! Buenos días, soy Ana. 116 121

Practica