JS 17: This y Context

Domina el concepto más confuso de JavaScript: el valor de "this"

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.

?? Regla de oro: 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"
Regla: Usa arrow functions para callbacks y funciones anidadas, pero NO para métodos de objetos.

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

Dominar this es crucial para trabajar con clases, event handlers y código orientado a objetos en JavaScript.

Volver a JS