1. ¿Qué es "this"?
this es una palabra clave especial que se refiere al contexto de ejecución
actual. Su valor cambia dependiendo de CÓMO se llama la función.
this NO se refiere a la función misma, se refiere al
objeto desde donde se llamó.
Ejemplo Inicial
const persona = {
nombre: 'Ana',
saludar: function() {
console.log('Hola, soy ' + this.nombre);
}
};
persona.saludar(); // "Hola, soy Ana"
// 'this' apunta a 'persona'
2. Las 4 Reglas de "this"
El valor de this se determina por CÓMO se llama la función:
1. Binding por Defecto (Default Binding)
function mostrarThis() {
console.log(this);
}
mostrarThis(); // window (en navegador) o global (en Node.js)
// En modo estricto
'use strict';
function mostrarThisEstricto() {
console.log(this);
}
mostrarThisEstricto(); // undefined
2. Binding Implícito (Implicit Binding)
Cuando se llama como método de un objeto:
const usuario = {
nombre: 'Carlos',
edad: 30,
obtenerInfo: function() {
return `${this.nombre} tiene ${this.edad} años`;
}
};
console.log(usuario.obtenerInfo()); // "Carlos tiene 30 años"
// 'this' apunta a 'usuario'
3. Binding Explícito (call, apply, bind)
function presentarse() {
console.log(`Hola, soy ${this.nombre}`);
}
const persona1 = { nombre: 'Laura' };
const persona2 = { nombre: 'Pedro' };
// call: llama la función CON el 'this' que le indiques
presentarse.call(persona1); // "Hola, soy Laura"
presentarse.call(persona2); // "Hola, soy Pedro"
4. Binding con new (Constructor)
function Persona(nombre, edad) {
this.nombre = nombre;
this.edad = edad;
}
const persona = new Persona('Ana', 25);
console.log(persona.nombre); // "Ana"
// 'this' apunta al nuevo objeto creado
3. call(), apply() y bind()
Métodos para controlar explícitamente el valor de this:
call() - Invocar con This Personalizado
function saludar(saludo, puntuacion) {
console.log(`${saludo}, ${this.nombre}${puntuacion}`);
}
const persona = { nombre: 'María' };
// Sintaxis: func.call(thisArg, arg1, arg2, ...)
saludar.call(persona, 'Hola', '!'); // "Hola, María!"
saludar.call(persona, 'Buenos días', '.'); // "Buenos días, María."
apply() - Igual que call(), pero con Array
function calcularTotal(impuesto, descuento) {
return this.precio * (1 + impuesto) * (1 - descuento);
}
const producto = { nombre: 'Laptop', precio: 1000 };
// Sintaxis: func.apply(thisArg, [arg1, arg2, ...])
const total = calcularTotal.apply(producto, [0.21, 0.10]);
console.log(total); // 1089 (1000 * 1.21 * 0.90)
// Diferencia con call
const totalCall = calcularTotal.call(producto, 0.21, 0.10); // argumentos separados
bind() - Crear Nueva Función con This Fijo
const coche = {
marca: 'Toyota',
obtenerMarca: function() {
return this.marca;
}
};
const obtenerMarca = coche.obtenerMarca;
console.log(obtenerMarca()); // undefined (pierde el contexto)
// bind() crea una NUEVA función con 'this' fijo
const obtenerMarcaFijo = coche.obtenerMarca.bind(coche);
console.log(obtenerMarcaFijo()); // "Toyota"
// bind() también permite aplicación parcial
function multiplicar(a, b) {
return a * b;
}
const duplicar = multiplicar.bind(null, 2); // 'a' siempre será 2
console.log(duplicar(5)); // 10
console.log(duplicar(10)); // 20
🛡️ Resumen: Call vs Apply vs Bind
| Método | Invocación | Argumentos | Uso principal |
|---|---|---|---|
call |
Inmediata | Uno a uno (coma) | Cuando sabes cuántos args tienes. |
apply |
Inmediata | Como un Array [] |
Cuando los args vienen en una lista. |
bind |
Diferida (Crea copia) | Uno a uno (coma) | Para callbacks o aplicaciones parciales. |
Truco mnemotécnico: Apply = Array. Call = Coma.
4. El Problema con Callbacks
Al pasar métodos como callbacks, se pierde el contexto:
? Problema
const usuario = {
nombre: 'Juan',
saludar() {
console.log(`Hola, ${this.nombre}`);
}
};
usuario.saludar(); // "Hola, Juan" ?
// Pasar como callback
setTimeout(usuario.saludar, 1000); // "Hola, undefined" ?
// Se pierde el contexto de 'usuario'
? Solución 1: Arrow Function Wrapper
setTimeout(() => usuario.saludar(), 1000); // "Hola, Juan" ?
? Solución 2: bind()
setTimeout(usuario.saludar.bind(usuario), 1000); // "Hola, Juan" ?
? Solución 3: Variable de Respaldo
const usuario = {
nombre: 'Juan',
saludar() {
const self = this; // Guardar referencia
setTimeout(function() {
console.log(`Hola, ${self.nombre}`);
}, 1000);
}
};
usuario.saludar(); // "Hola, Juan" ?
5. Arrow Functions y "this"
Las arrow functions NO tienen su propio this. Heredan el
this del ámbito donde fueron definidas (lexical this).
Diferencia Clave
const obj = {
nombre: 'Objeto',
// Función tradicional: tiene su propio 'this'
metodoTradicional: function() {
console.log(this.nombre); // "Objeto"
setTimeout(function() {
console.log(this.nombre); // undefined (this = window)
}, 100);
},
// Arrow function: hereda 'this' del contexto
metodoArrow: function() {
console.log(this.nombre); // "Objeto"
setTimeout(() => {
console.log(this.nombre); // "Objeto" (hereda 'this')
}, 100);
}
};
obj.metodoTradicional();
obj.metodoArrow();
?? Problema con Arrow en Métodos
const persona = {
nombre: 'Ana',
// ? Arrow function como método
saludar: () => {
console.log(this.nombre); // undefined
// 'this' apunta al contexto donde se definió el objeto (global)
},
// ? Usar función tradicional para métodos
saludarBien: function() {
console.log(this.nombre); // "Ana"
}
};
persona.saludar(); // undefined
persona.saludarBien(); // "Ana"
6. Casos de Uso Reales
1. Event Handlers en DOM
class Contador {
constructor() {
this.conteo = 0;
this.boton = document.querySelector('#miBoton');
// ? Problema: pierde contexto
// this.boton.addEventListener('click', this.incrementar);
// ? Solución 1: bind
this.boton.addEventListener('click', this.incrementar.bind(this));
// ? Solución 2: arrow function
this.boton.addEventListener('click', () => this.incrementar());
}
incrementar() {
this.conteo++;
console.log(this.conteo);
}
}
2. Métodos en Clases
class Usuario {
constructor(nombre) {
this.nombre = nombre;
}
saludar() {
console.log(`Hola, soy ${this.nombre}`);
}
// Arrow function como propiedad de clase (mantiene 'this')
saludarArrow = () => {
console.log(`Hola, soy ${this.nombre}`);
}
}
const user = new Usuario('Carlos');
user.saludar(); // "Hola, soy Carlos"
const metodo = user.saludar;
metodo(); // Error: this.nombre is undefined
const metodoArrow = user.saludarArrow;
metodoArrow(); // "Hola, soy Carlos" ?
3. Array Methods con Context
const calculadora = {
base: 10,
numeros: [1, 2, 3, 4, 5],
sumarABase() {
// ? Problema con función tradicional
return this.numeros.map(function(num) {
return num + this.base; // Error: this.base is undefined
});
},
sumarABaseArrow() {
// ? Arrow function hereda 'this'
return this.numeros.map(num => num + this.base);
},
sumarABaseBind() {
// ? Usar bind
return this.numeros.map(function(num) {
return num + this.base;
}.bind(this));
}
};
console.log(calculadora.sumarABaseArrow()); // [11, 12, 13, 14, 15]
7. Debugging de "this"
Estrategias para encontrar problemas con this:
1. Añadir console.log
function miFuncion() {
console.log('this es:', this);
console.log('Constructor:', this.constructor.name);
// Identificar qué está pasando
}
2. Usar debugger
const obj = {
metodo() {
debugger; // Se para aquí, inspecciona 'this' en DevTools
console.log(this);
}
};
3. Verificar Modo Estricto
'use strict';
function test() {
console.log(this); // undefined en modo estricto
}
test();
8. Tabla de Referencia Rápida
| Contexto | Valor de "this" | Ejemplo |
|---|---|---|
| Global | window (navegador) | function f() { console.log(this); } f(); |
| Método de objeto | El objeto | obj.metodo() |
| Constructor (new) | Nuevo objeto | new Clase() |
| call/apply | Primer argumento | func.call(obj) |
| bind | Fijo (no cambia) | func.bind(obj) |
| Arrow function | Hereda del contexto | () => this |
| Event handler | El elemento DOM | btn.addEventListener('click', func) |
| Modo estricto (global) | undefined | 'use strict'; f(); |
9. Ejercicios Prácticos
Ejercicio 1: ¿Qué imprime?
const obj = {
nombre: 'Objeto',
metodo1: function() {
console.log(this.nombre);
},
metodo2: () => {
console.log(this.nombre);
}
};
obj.metodo1(); // ¿?
obj.metodo2(); // ¿?
const func1 = obj.metodo1;
func1(); // ¿?
Ejercicio 2: Arreglar el Callback
const usuario = {
nombre: 'Laura',
saludar() {
setTimeout(function() {
console.log(`Hola, ${this.nombre}`);
}, 1000);
}
};
usuario.saludar(); // Imprime "Hola, undefined"
// Arregla el código para que imprima "Hola, Laura"
Ejercicio 3: Implementar una Clase
class Temporizador {
constructor(segundos) {
this.segundos = segundos;
}
iniciar() {
// Implementa un countdown que use 'this.segundos'
// y lo decremente cada segundo
// Debe imprimir cada segundo hasta llegar a 0
}
}
const timer = new Temporizador(5);
timer.iniciar();
Resumen
- ?
thisdepende de CÓMO se llama la función, no DÓNDE se define - ? En métodos de objetos,
this= el objeto - ? En funciones normales globales,
this= window (o undefined en strict mode) - ?
call()yapply()invocan conthispersonalizado - ?
bind()crea nueva función conthisfijo - ? Arrow functions NO tienen
thispropio (heredan del contexto) - ?? No uses arrow functions para métodos de objetos
- ? Usa arrow functions para callbacks y funciones anidadas
Dominar this es crucial para trabajar con clases, event handlers y código
orientado a objetos en JavaScript.