bind(), call() y apply() en JavaScript: controlar el valor de this

call(), apply() y bind() son tres métodos que toda función JavaScript hereda de Function.prototype. Los tres permiten controlar manualmente el valor de this cuando se llama a una función. La diferencia está en cómo se pasan los argumentos y en si la función se ejecuta inmediatamente o se devuelve para usarla después.

call(): ejecutar con this y argumentos separados

call(thisArg, arg1, arg2, ...) llama a la función inmediatamente con el this que especifiques. Los argumentos se pasan uno por uno:

function presentar(saludo, puntuacion) {
  console.log(`${saludo}, soy ${this.nombre}${puntuacion}`);
}

const persona = { nombre: 'Elena' };

presentar.call(persona, 'Hola', '!');
// "Hola, soy Elena!"

// Caso real: tomar prestado un método de otro objeto
const arrayLike = { 0: 'a', 1: 'b', 2: 'c', length: 3 };
const arr = Array.prototype.slice.call(arrayLike);
console.log(arr);  // ['a', 'b', 'c']

apply(): como call() pero con array de argumentos

apply(thisArg, [args]) es idéntico a call() salvo que los argumentos se pasan como un array. Era muy útil antes de que existiera el spread operator:

function sumar(a, b, c) {
  return a + b + c;
}

const numeros = [1, 2, 3];

// Con apply: pasar array como argumentos
console.log(sumar.apply(null, numeros));  // 6

// Hoy se prefiere spread:
console.log(sumar(...numeros));  // 6

// apply sigue siendo útil con Math.max/min:
const valores = [3, 1, 4, 1, 5, 9, 2, 6];
console.log(Math.max.apply(null, valores));  // 9
// O con spread:
console.log(Math.max(...valores));  // 9

bind(): crear una nueva función con this fijo

bind(thisArg, ...args) no ejecuta la función: devuelve una nueva función con el this permanentemente vinculado. Es la solución más limpia para el problema del callback:

const boton = {
  texto: 'Enviar',
  manejarClick() {
    console.log('Clic en: ' + this.texto);
  }
};

// Sin bind: this se pierde en el event listener
document.addEventListener('click', boton.manejarClick);
// "Clic en: undefined"

// Con bind: this siempre apunta a boton
document.addEventListener('click', boton.manejarClick.bind(boton));
// "Clic en: Enviar"

Partial application con bind()

Además de fijar this, bind() permite fijar argumentos de forma permanente, creando funciones especializadas (partial application):

function calcularPrecio(impuesto, precio) {
  return precio * (1 + impuesto);
}

// Crear versiones especializadas con impuesto fijo
const conIva = calcularPrecio.bind(null, 0.21);
const conIvaReducido = calcularPrecio.bind(null, 0.10);

console.log(conIva(100));          // 121
console.log(conIvaReducido(100));  // 110

// En React es un patrón muy frecuente para pasar parámetros a handlers:
// 

bind() no funciona con arrow functions

Las arrow functions no tienen su propio this, así que bind(), call() y apply() no pueden cambiar su contexto. El primer argumento (thisArg) se ignora:

const objeto = { valor: 42 };

const flecha = () => this.valor;

// bind no tiene efecto sobre el this de la arrow
const vinculada = flecha.bind(objeto);
console.log(vinculada());  // undefined (no 42)

// call tampoco:
console.log(flecha.call(objeto));  // undefined

El truco de slice.call para arrays

Antes de Array.from() y el spread operator, el patrón para convertir arguments en un array real era:

function ejemplo() {
  // arguments es un objeto array-like, no un array
  const args = Array.prototype.slice.call(arguments);
  return args.map(x => x * 2);
}

console.log(ejemplo(1, 2, 3));  // [2, 4, 6]

// Hoy se usa rest parameters:
function ejemploModerno(...args) {
  return args.map(x => x * 2);
}

La distinción práctica: usa call() cuando tienes los argumentos por separado, apply() cuando ya tienes un array y necesitas compatibilidad, y bind() cuando quieres crear una función para usar después o fijar argumentos parciales.

COMPARTE ESTE ARTÍCULO

COMPARTIR EN FACEBOOK
COMPARTIR EN TWITTER
COMPARTIR EN LINKEDIN
COMPARTIR EN WHATSAPP