JavaScript 00: 6. callback, apply, call y bind

Jugando con las funciones en js

Waterhouse

archivado en: JavaScript / 9 marzo, 2015 / taller:

En las próximas entradas de esta serie dedicada a profundizar en los objetos de javaScript comenzaré con patrones de diseño, pero antes conviene que veamos dos conceptos algo abstrusos de javaScript. Uno es el this mutante y otro es el cacao de las funciones callback y los métodos apply, call y bind.

Funciones callback

Como es sabido, las funciones son fragmentos de código encapsulados que realizan una o más acciones. Y las funciones pueden recibir parámetros o argumentos, esto es, datos que se trabajarán dentro de la propia función.

function suma(numero1, numero2) {

console.log(numero1+numero2)

}

suma(3, 2)// 5

En js, dentro de una función podemos ejecutar otra función.

function suma(numero1, numero2) {

console.log(numero1+numero2);

function resta(numero1, numero2) {

console.log(numero1-numero2);

}

resta(3, 2)// 1

}

suma(3, 2); // 5

Pero por limpieza del código, también podríamos haber sacado la segunda función fuera y habérsela pasado a la primera función como argumento, que es una característica de js muy interesante.

function resta(numero1, numero2) {

console.log(numero1-numero2);

}

function suma(numero1, numero2, funcionCallback) {

funcionCallback(numero1, numero2); // 1

}

suma(3, 2, resta);

De hecho, podríamos haberle pasado directamente una función anónima, que es una sintaxis que seguro recuerdan los amantes de jQuery:

function foo(numero1, numero2, bazinga) {

bazinga(numero1, numero2);

};

foo(3, 2, function (numero1, numero2) {

console.log(numero1-numero2); // 1

});

Pues son este tipo de funciones las que en js se denominan funciones callback, que en inglés significa algo así como «llamadas de vuelta».

Un this mutante

Para jugar con funciones que van y vienen también contamos con tres métodos de muy chulos del objeto tipo function, los cuales se basan en el manejo peculiar de this que se hace en javaScript.

En cualquier lenguaje es normal encontrarse con la palabra reservada this para referirse la propia clase donde están declarados métodos y propiedades. Por ejemplo, en php podríamos hacer algo así:

class miClase {

protected $miVariable ="foo";

function devuelveFoo () {

return $this->miVariable; // equivale a decir, "la propiedad miVariable de esta clase"

}

}

En javaScript también existe un this, solo que, como no podría ser de otra manera, se comporta de forma diferente y un tanto alocada. Por ejemplo, fuera de cualquier función y objeto, this hace referencia a window:

console.log(this); // Window

Pero si escribimos algo así, ¡también aparece de nuevo window!

function foo() {

console.log(this); // Window

}

foo();

Sin embargo, a veces sí que parece hacer referencia a la propia función:

var dum = function() {

console.log(this); // dum {}

this.bam = function() {

console.log(this); // dum {bam: function}

}

}

miDum = new dum();

miDum.bam();

Para entender este comportamiento extraño, lo primero que debemos tener en cuenta es que this suele hacer referencia al objeto desde el que se invoca una función o método, es decir, al contexto de la función. Esto se entiende bien si nos fijamos en este ejemplo, donde asignamos la misma función a dos objetos distintos:

function bazinga() {

console.log(this.foo);

}

/* Aquí el this de bazinga hace referencia a window, porque este es objeto desde el que ha sido invocada la función */

console.log(this);

var miObjeto = {

foo: "bar",

metodo: bazinga

}

var otroObjeto = {

foo: "zasprist",

metodo: bazinga

}

/* Aquí this apunta a miObjeto */

miObjeto.metodo(); // bar

/* Aquí this apunta a otroObjeto */

otroObjeto.metodo(); // zasprist

 

/* y aquí a OtroMas */

var OtroMas = function() {

this.foo = "hey";

this.metodo = bazinga;

}

otroMas = new OtroMas();

otroMas.metodo(); // hey

Si este fuera siempre el comportamiento de this, aunque es algo enrevesado, podríamos manejarlo con cierta soltura. Sin embargo hay una especie de bug, por el que se pierde su referencia cuando se utiliza en una función desde un objeto. Por ejemplo:

var MiClase = function() {

this.foo = "bar";

this.bazinga = function() {

console.log(this.foo); // bar

console.log(this); // MiClase

function yuk() {

console.log(this.foo); // undefined

console.log(this); // Window o0

}

yuk();

}

}

miInstancia = new MiClase();

miInstancia.bazinga();

Para resolver este problema, muchas veces se cachea al principio el valor de this en una variable que, por convención, se suele llamar self o that.

var MiClase = function() {

var self = this;

this.foo = "bar";

this.bazinga = function() {

function yuk() {

console.log(self.foo); // bar

console.log(self); // MiClase

}

yuk();

}

}

Bueno, volveré sobre este tema en próximas entradas porque es particularmente complejo, pero de momento nos basta con entender que this suele apuntar al contexto de la función... salvo que lo cambiemos : ).

apply, call y bind

Con el método call() podemos cambiar el valor de this, que pasa ser el que indiquemos en su primer parámetro. Además, si fuera necesario, podemos pasar los n parámetros que demande la función. En un ejemplo muy sencillo:

function sumar(numero) {

/* this = 2 y numero = 5 */

console.log(this+numero); // 7

}

sumar.call(2, 5);

En otro igual de sencillo:

function convierteMayuscula() {

return this.cadena.toUpperCase();

}

miObjeto = {

cadena: "foo"

}

otroObjeto = {

cadena: "bar"

}

console.log( convierteMayuscula.call(miObjeto) ); // FOO

console.log( convierteMayuscula.call(otroObjeto) ); // BAR

El método apply() es muy parecido, solo que además del valor de this se le puede pasar un array de argumentos. Y esto es muy interesante cuando los desconocemos o carecen de importancia, sobre todo combinado con el objeto arguments, que es una especie de array con todos los argumentos que ha recibido una función.

Por ejemplo, así podríamos calcular la suma de todos los ítems de un array a los que sumamos a su vez 2.

function suma() {

var resultado = 0;

var i = 0;

var len = arguments.length;

for (; i<len; i++) {

resultado += this + arguments[i];

}

return resultado;

}

var numeros = [1, 4, 5];

console.log( suma.apply(2, numeros) ); // 16

Y ya solo nos falta por ver bind(). Dicho de la forma menos liosa que puedo: este método crea una nueva función idéntica a la original, pero en la que podemos definir qué valor tendrá this. En el ejemplo más sencillo que se me ocurre:

function bazinga() {

console.log(this.cantar);

}

/* Aquí bazinga es undefined, ya que no tiene la propiedad cantar */

bazinga();

var miObjeto = {

cantar: "Oh la la!"

};

miObjeto.miMetodo = bazinga.bind(miObjeto);

/* Aquí bazinga es Oh la la!, ya que ahora this sí que está definido, es miObjeto */

miObjeto.miMetodo();

Bind nos permite solucionar algunas situaciones complejas, como las que se producen con el uso de this en los callbacks, pero eso ya guarda relación con los patrones de diseño que comenzaremos a ver en las siguientes entradas. Por hoy está bien, que es tarde :P.

|| Tags: , , , ,

valoración de los lectores sobre JavaScript 00: 6. callback, apply, call y bind

  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • estrellica valoración positiva
  • 4.9 sobre 5 (11 votos)

¿Te ha parecido útil o interesante esta entrada?
dormido, valoración 1 nadapensativo, valoración 2 un poco sonrisa, valoración 3 a medias guiño, valoración 4 bastante aplauso, valoración 5 mucho

Tú opinión es muy importante, gracias por compartirla!

Una respuesta a “JavaScript 00: 6. callback, apply, call y bind

  1. Pingback: react revisitado: 05. Eventos | The Bit Jazz Band