¿Para qué sirven los métodos .call() y .apply() en JavaScript?
Tags: javascript, oop
Published: 2019-10-31 23:35:09

---
title: ¿Para qué sirven los métodos .call() y .apply() en JavaScript?
published: true
description: Un paseo por los fundamentos de JavaScript
tags: javascript, oop
cover_image: https://miro.medium.com/max/799/0*4o_PPMhPEt3HZ3YR.
---

He de admitir que el desconocimiento de estos métodos, junto con la sintáxis Class fueron los mayores desencadenantes de querer profundizar más en los fundamentos del lenguaje.

Ahora que te los he mencionado, los comenzarás a ver en todas partes. En realidad ya estaban ahí pero, ¿sabías que hacen?. Pues son muy populares, y mucho más en proyectos ES5 y previos.

Estos métodos forman parte de la base de la programación orientada a objetos de JavaScript, y son cruciales para entender el lenguaje y es una pena que las sintáxis como Class y la keyword new los estén dejando en deshuso. Y digo que es una pena porque son fundamentales y muy importantes en el sistema de herencia prototipal, mientras que los otros son sugar syntax que lo único que hacen es oscurecer el lenguaje.

Antes de comenzar, estos dos métodos están disponibles en el prototype del objeto global Function:

> Function.prototype.call
  Function.prototype.apply

En este post mostraré ejemplos de uso para que lo entiendas y pongas a prueba.

# Métodos call() y apply()

Antes de nada y te voy a ahorrar dolores de cabeza, son exactamente lo mismo. **La única diferencia reside en el segundo argumento**, donde call() será un listado de argumentos infinito y apply() será un array.

```js
fn.call(this, arg1, arg2, arg3...)
fn.apply(this, [arg1, arg2, arg3...])
```

Un tip que me ayuda a recodar cuál es cuál es la letra C de call, la cual **me recuerda a comas**; la a de apply, **me recuerda a array**

Vamos a un ejemplo con call(). Vamos a crear una pseudoherencia clásica. Se trata de una pseudoherencia clásica porque estamos definiendo la estructura del objeto final, "instancia" de Person.

{% runkit %}
function Human(gender) {
  this.gender = gender;
  this.isAlive = true;
}

function Person(gender, age) {
  // this = {}
  Human.call(this, gender);
  // this = { gender: 'male', isAlive: true }
  this.age = age;
  // this = { gender: 'male', isAlive: true, age: 18 }
  return this;
}

const alberto = Person.call({}, 'male', 18);

console.log(alberto);
process.exit(0)
{% endrunkit %}

De manera sencilla, llamamos a la función Person a la cual le pasamos primero un objeto vacío como _this_, y a continuación **los argumentos necesarios por la función**.

```js
const alberto = Person.call({}, 'male', 18);
```

¿Sabes que habría pasado si en vez de poner {} hubiésemos puesto _this_? Habría pasado lo siguiente:

{% runkit %}
function Human(gender) {
  this.gender = gender;
  this.isAlive = true;
}

function Person(gender, age) {
  // this = global: { ... }
  Human.call(this, gender);
  // this = global: { ..., gender: 'male', isAlive: true }
  this.age = age;
  // this = global: { ..., gender: 'male', isAlive: true, age: 18 }
  return this;
}

const alberto = Person.call(this, 'male', 18);

console.log(alberto);
process.exit(0)
{% endrunkit %}

Como puedes ver alberto ahora tiene un montón de propiedades nuevas, y esto se debe a que _this_ en el momento de ser ejecutado con .call hace referencia al objeto window del navegador (o global si estamos en Node como es el caso), y por lo tanto estaríamos pasando como contexto a Person un objeto no deseado.

![](https://media.giphy.com/media/t8m0LMOd4nmKs/giphy.gif)

¿Y si en vez de usar _this_ y call(), llamo a la función directamente?

```js
function Human(gender) {
  this.gender = gender;
  this.isAlive = true;
}

function person(gender, age) {
  // Está recibiendo un this implícito
  // this = global: { ... }
  Human.call(this, gender);
  // this = global: { ..., gender: 'male', isAlive: true }
  this.age = age;
  // this = global: { ..., gender: 'male', isAlive: true, age: 18 }
  return this;
}

const alberto = person('male', 18); // Invocando a la función sin call()
/*
Es lo mismo! La transformación la ejecuta el parser de JS (internals)
  person('male', 18) 'implícito' === 'explícito' person.call(this, 'male', 18)
*/

console.log(alberto);
```

Sucedería exactamente lo mismo. Podrás observar varias cosas:
1. No he usado el sandbox porque por lo visto tiene algún mecanismo de seguridad para prevenir esta práctica, dado que supone un riesgo de seguridad. Puedes probarlo en tu navegador o en Node.

2. He renombrado Person a person, y eso se debe a lo mismo. Se creó una regla para que no se pueda llamar a las funciones directamente si comienzan en letra capital, dado que puede darse el caso de que un desarrollador llame a la función directamente y el _this_ quede referenciado al global/window.

**Hay que andarse con ojo a la hora de usar _this_ .** Y este es uno de los principales motivos por los que se empiezan a crear nuevos mecanismos como new y Class, para evitar equivocaciones y proveer de una opción más sencilla a los desarrolladores que vienen de lenguajes OOP de clases.

Ahora toca el turno a explicar qué sucede dentro de Person. Retomamos el primer ejemplo. Como puedes ver volvemos a hacer uso de call()^2, pero esta vez en vez de {} usamos la palabra _this_^3. El _this_ siempre depende del contexto desde el cual se ha ejecutado, y en este caso proviene del call(_this_)^1 de **alberto**, que es {}.

(el símbolo > lo uso para que podáis encontrar lo que digo)

```js
function Person(gender, age) {
  // this = {}
  Human.2>call(3>this, gender);
...
const alberto = Person.1>call(1>{}, 'male', 18)
```

Por lo tanto, continuando la llamada, Human continúa recibiendo el objeto vacío a través del contexto^3 que estamos enviando explícitamente a través de call(this)^2

Aprovecho para mencionar, que es usual decir _contexto_ y referirnos a _this_, dado que todo _this_ dependerá del contexto desde el que se le llame.

> Todo **this** dependerá del contexto desde el que se le llame. Es por lo tanto una referencia contextual.

```js
function Human(gender) {
  3> // this = {} ( proviene de Human.call(this, gender) )
  this.gender = gender;
  this.isAlive = true;
}

function Person(gender, age) {
  // this = {}
  Human.2>call(2>this, gender);
...
const alberto = Person.1>call(1>{}, 'male', 18)
```

Ahora viene lo bonito 💕❤🌹🎉en JavaScript existe una técnica llamada Aumentación. Es más fácil leerla y encontrarla como "Augmentation", su variante inglesa.

Lo que está haciendo Human es aumentar el contexto desde el que se le llama. En otras palabras, aumentar el _this_, añadirle más propiedades (también podrían ser métodos).

Y ahora lo no tan bonito 👺, si queremos aprovechar el potencial de JavaScript, hay que saber cuándo aumentar el contexto. Lo digo porque al final, acaba convirtiéndose en una composición que no aprovecha los prototypes. Digamos que el call() sería como un super(). Pero esto es para otro tema.

Ahora, el contexto tendrá dos propiedades nuevas, que son **gender** y **isAlive**. El this que hay en person ha aumentado. Volvemos a aumentar el contexto añadiendo la propiedad age^1. Y finalmente retornamos^2 el contexto aumentado.

```js
function Person(gender, age) {
  ...
  // this = { gender: 'male', isAlive: true }
  this.age = age^1;
  // this = { gender: 'male', isAlive: true, age: 18 }
  return this^2;
}
```


¿Has entendido las diferencias entre estos dos métodos? ¿Me animo a crear un post con más ejemplos?

Espero tus comentarios y que te haya sido de utilidad.