Funciones en Javascript

Para mí, Javascript es un lenguaje muy especial. Todos los que llevamos un par de años en esto de la programación, a lo largo de nuestros estudios y de nuestra vida profesional hemos tenido la oportunidad y la necesidad de aprender o familiarizarnos con algunos cuantos lenguajes de programación. Cada uno de estos lenguajes, tienen sus ventajas y sus inconvenientes, detalles que nos han encantado y otros que nos han sacado de nuestras casillas. Dentro de los que yo me he tropezado en mi camino, Javascript tiene un lugar especial ¿Por qué digo esto?:

  • Es un lenguaje interpretado. Los lenguajes interpretados tienen su propia idiosincrasia.
  • Se concibió para ejecutarse en el navegador (que es un entorno de ejecución ciertamente peculiar) aunque ya ha dado el salto para ejecutarse en el servidor.
  • Su estructura sintáctica es muy abierta. Te ofrece ciertas construcciones, pero muy abiertas. Esta sintaxis tan abierta junto con el hecho de ser interpretado te da una enorme expresividad… pero puede suponer un infierno para depurarlo o para mantenerlo (cuando el tamaño del código crece).

(Por supuesto, estos son opiniones particulares, estaré encantado si alguien no comparte mi opinión).

Dentro de esta flexibilidad de su estructura, para mí uno de los elementos más fascinantes son las funciones. Si tuviera que resumir en una frase el motivo por el que son tan fascinantes sería «porque las funciones javascript son objetos de primer orden».

Las funciones javascript como objetos de primer orden

¿Qué quiero decir con esta frase? A ver si me consigo explicar:

  • En la mayoría de lenguajes que me he encontrado, las funciones son una estructura a la que se asocia un identificativo, que te permite encapsular un trozo de código y que se puede invocar usando el identificativo asociado y pasándole una serie de parámetros definidos en la declaración.
  • En Javascript, podemos declarar funciones al «estilo clásico» que he mencionado antes … pero como una función es un objeto de tipo function… también podemos declararla de otro modo alternativo ligeramente distinto.

Así, una función hello podemos declararla de dos formas distintas:

Al «estilo clásico»:

Pero también podemos declararla así:

En esencia es casi lo mismo … pero no es exactamente igual:

Esto, funciona correctamente:

Pero esto no funciona:

El motivo por el que no funciona es que en la primera versión, el intérprete de Javascript la convierte por nosotros:

(O sea, el intérprete define la variable hello al principio del código).

Pero en nuestra segunda versión nosotros hemos definido la variable hello manualmente, por lo que el intérprete no modifica nada … pero como la hemos invocado previamente a su definición… pues la ejecución falla.

Importante: cuando declaramos una función al «estilo clásico», no la terminamos con ;. Pero cuando la definimos como una variable, que terminamos la definición con ;. Los intérpretes de Javascript suelen ser bastante permisivos y no le dan importancia a esos detalles. Pero creo recordar que IE8 se queja.

Creo que ha quedado claro que los términos «función» y “objeto tipo function” son equivalentes en Javascript. A lo largo de esta entrada, emplearé el término «función» cuando la usemos de forma «clásica» y el término “objeto de tipo function” para remarcar que es un objeto y que se puede manipular como cualquier otro objeto.

Distinguir entre manipular la variable de tipo function y ejecutar la función asociada

Así, pues, tenemos variables que apuntan a objetos de tipo function. Incluso cuando declaramos una función al «estilo clásico», ya hemos visto que estamos declarando variables que apuntan a objetos de tipo function. Este dato, como veremos, nos puede dar mucha expresividad para nuestros programas. Pero es fundamental que tengamos claro qué es manipular la variable y qué es ejecutar la función referenciada por la variable. A ver si puedo explicar este punto con un ejemplo simple:

  • En la línea 1, he definido una variable hello a la que he asignado un objeto de tipo function.
  • En la línea 2, he mostrado en la consola de depuración hello (que es la variable), y el valor mostrado es un objeto de tipo function.
  • En la línea 3, he mostrado en la consola de depuración hello() (o sea, he invocado a la función asignada a la variable hello) y el valor mostrado es 'hello'.

Como veis, escribir unos simples () marcan una diferencia abismal a la hora de ejecutar el código.

Manejando objetos de tipo function

Como las funciones son objetos, eso quiere decir que puedo hacer con ellas cualquier tipo de operación que puedo hacer con cualquier otro tipo de objeto. Por ejemplo:

Efectivamente, puedo asignar a mi variable a el valor de mi variable hello. Con lo cual, tengo ambas variables referenciando al mismo objeto function. Puedo usar hello() o a() para invocar a la misma función.

Array de funciones

En este pequeño trozo de código, hemos definido un array. Y le hemos añadido cuatro objetos de tipo function. El primero, lo hemos definido previamente y hemos usado la variable para añadirlo al array. Pero los tres siguientes los hemos definido sobre la marcha cuando los añadíamos al array. Son funciones anónimas (acostumbraros a este tipo de operaciones, porque en Javascript las usaréis mucho).

Una vez añadidas nuestros objetos function al array, lo hemos recorrido e invocado a cada uno de los elementos. Fijaros en la nomenclatura: myFuncs[i]('jmj');:

  • Accedo al elemento i de mi array myFuncs (myFuncs[i]).
  • Una vez que he accedido, lo ejecuto pasándole «jmj» como argumento ('jmj').

Los objetos tipo function pueden ser argumentos pasados a otras funciones

Veamos este nuevo fragmento de código:

Unos cuantos detalles:

  • En la versión 1, cuando invocamos a parseArray, le pasamos como argumento parser (el nombre de la variable que referencia al objeto de tipo function) y no parser() (si hiciéramos esto último, lo que haríamos es ejecutar la función).
  • La versión 2 es lo mismo que la versión 1 excepto que le pasamos como argumento una función anónima (¿para qué declarar la función previamente, si sólo vamos a usarla una vez y es una función sencillita de declarar? nos ahorramos la declaración previa).

Pasar objetos function como argumentos a funciones que se ejecutan asíncronamente

El hecho de poder pasar objetos function a funciones se usa extensamente en Javascript. El motivo es que muchas de las operaciones que realizamos en nuestros programas Javascript son operaciones asíncronas ¿Qué quiero decir con esto? Que pedimos que se ejecuten… pero no sabemos cuando se terminarán de ejecutar. Ejemplos de estas operaciones asíncronas son:

  • Operaciones que afectan a comunicaciones (por ejemplo, todas las llamadas Ajax).
  • Operaciones que manipulan el DOM.

El problema de las operaciones asíncronas (o sea, que no sé cuándo van a terminar) es que si necesito hacer una segunda operación supeditada a cuando terminen ¿cómo sé cuándo realizar esta segunda operación? Por ejemplo, a mí me gustaría escribir en mi código:

Pero, como he dicho, esto no funciona: myAjaxCall() se ejecuta… pero el programa pasa a la siguiente instrucción sin esperar el resultado de la operación, con lo que la llamada a process(resp) no es válida porque es realizada sin que halla llegado el resultado de myAjaxCall() y por lo tanto el valor de resp es incorrecto.

¿Cómo hay que escribir este tipo de operaciones que esperan un resultado asíncrono?:

A ver si puedo explicarlo:

  • Realizo la llamada a myAjaxCall pero como sé que es una operación asíncrona que no sé cuándo termina, le pido a la llamada: “cuando termines, invoca al objeto function que te paso como parámetro”.
  • Cuando la llamada termine, invocará a este objeto function y le enviará como parámetro el valor resp que es el resultado de la operación asíncrona. Ahora, dentro de mi objeto function sí puedo invocar a mi función process y le puedo pasar este parámetro resp que ahora sí que estará correctamente relleno (insisto, todo esto ocurrirá una vez que la operación asíncrona termine).

La gestión de las operaciones asíncronas, pueden ser muy complejo. El motivo de esta complejidad es que queremos responder a eventos que terminan asíncronamente en un futuro pero queremos responder con la información que disponíamos cuando solicitamos la operación. Y las condiciones ya no son las mismas (tendemos a pensar de forma secuencial, nos cuesta pensar de forma asíncrona). Por eso, para gestionar correctamente las operaciones asíncronas hay que tener en cuenta otros dos conceptos fundamentales de Javascript: el alcance de las variables y el contexto en que se ejecutan las funciones (no explico más sobre ellos aquí, pero no tengo más remedio que al menos citar ambos conceptos).

Las funciones pueden devolver objetos de tipo function

Una función Javascript puede devolver un resultado (como cualquier otro lenguaje de programación). Pero como el tipo function es un tipo válido… puedo devolver como resultado una función. Veamos el siguiente código:

Este ejemplo es muy simple (el objetivo, es mostrar la operativa de la forma más clara posible) pero podemos realizar operaciones más complejas:

Javascript no cuenta con elementos constructivos como las variables estáticas de otros lenguajes de programación. Pero podemos conseguir resultados similares usando los mecanismos con los que sí cuenta. En este caso:

  • La función counter tiene una variable count. Devuelve un objeto function que cuando se invoque a) admite un parámetro value b) incrementará la variable count con este valor c) devolverá el nuevo valor de count.
  • Lo más importante, es que la variable count declarada dentro de counter es absolutamente privada e inaccesible para el resto del código. Si pido el valor de la variable count, me devuelve el valor de la variable declarada fuera de counter.

A mí, personalmente, me gusta usar los recursos que me da cada lenguaje para indicar qué variables son de uso privado y cuáles son de uso global. Con construcciones como ésta, puedo conseguirlo en Javascript.

Funciones invocadas inmediatamente

Lo he traducido al castellano pero su denominación en inglés es «immediately-invoked function expression» (también conocidas como «IIFE»). Y ¿qué es lo que son?. Creo que lo mejor es ver un ejemplo y explicarlo sobre el ejemplo:

¿Qué es lo que he hecho?:

  • Entre las líneas 3 a 6 he definido una función anónima. Dentro de dicha función, declara e inicializa una variable a.
  • Pero lo más interesante es que esa función la rodeo de paréntesis y le añado paréntesis al final ((function() {...})()) ¿Recordáis cuando antes explicaba la diferencia entre escribir variableQueApuntaAFuncion y variableQueApuntaAFuncion(). Efectivamente, al añadir los () al final lo que hago es ejecutar la función anónima nada más declararla.

«¿Y para qué quiero hacer esto, declarar una función anónima y ejecutarla directamente?» Fijaros que tengo dos variables con el mismo nombre a, una declarada dentro de la función anónima y otra declarada fuera. Cada una, tiene su propio valor y ninguna afecta a la otra. Son fragmentos de código absolutamente independientes.

«Bueno, sí, son fragmentos de código independientes ¿Y qué?». Este hecho, hay que considerarlo en el contexto del tamaño de tus programas Javascript:

  • Si el programa es pequeño y tú eres el único programador, es fácil conseguir que no haya conflictos con los nombres de las variables (por repetir, por ejemplo, el mismo nombre en dos fragmentos de tu código).
  • Pero si el programa es extenso o participan varios programadores o tienes que usar librerías de terceros, conseguir que no tengamos conflictos con los nombres de las variables y de las funciones, es un poco más complicado.
  • Si encapsulas tu código dentro de una IIFE, estás garantizando que las variables y funciones que declares dentro no afectarán al resto del código.

Para explicar este punto de forma más completa, debería hablar más extensamente sobre el alcance de las variables en Javascript… pero de momento me tengo que limitar a estas pequeñas pinceladas.

Puedes añadir objetos tipo function como propiedades a cualquier otro objeto

Una de las peculiaridades de Javascript es que en cualquier momento puedes extender una variable que no sea de tipo primitivo, añadiéndole propiedades sobre la marcha:

Lo mismo que le hemos añadido a este objeto myBirthday una propiedad name que hemos inicializado con una cadena, podíamos haberlo inicializado con un objeto de tipo function:

En este pequeño ejemplo, hemos creado un objeto myModule (inicialmente vacío) y le hemos añadido dos propiedades name y greeting (si fuera un lenguaje clásico de programación, esta última propiedad podríamos considerarla como un método). Después, hemos declarado dos variables name y greeting. Lo importante, es que no hay conflictos entre ellas aunque tienen los mismos nombres.

Usando esta técnica, también podemos encapsular nuestro código (variables y funciones) dentro de un solo objeto (al que podemos considerar un módulo) para evitar tener conflictos con el código de terceros.

Podemos usar funciones para crear nuevos objetos

Javascript no tiene un constructor para definir clases. Pero podemos crear nuevos objetos que tengan un comportamiento predefinido:

Sin entrar en muchos detalles:

  • Uso la función Person para definir que los objetos tienen una propiedad name y un método greetings.
  • Uso el operador new para crear dos objetos llamados jmj y brutus ambos basados en Person.
  • Puedo usar las propiedades y métodos predefinidos por Person en ambos objetos.

Fijaros que no escribo “jmj es de tipo Person” sino que escribo “jmj está basado en Person”. Lo que intento es dejar claro que Javascript no permite definir clases pero al menos permite al crear objetos a partir de un patrón predefinido. Y sólo quiero mencionar que dicho patrón es una función (explicar detenidamente este concepto, requeriría una o dos entradas sólo para ello).

¿Conclusiones?

Tal como mencionaba al comienzo de la entrada, Javascript es un lenguaje relativamente simple. Pero las construcciones que tiene permiten muchísima expresividad. En esta entrada, he intentado recopilar todas las manipulaciones con objetos de tipo function que me he ido encontrando (y es posible que se me olvide alguna y haya otras más que no conozca). Ahora sí, es nuestra resposabilidad como programadores el usar esta expresividad para escribir mejores programas Javascript.

4 comentarios a “Funciones en Javascript

    • Gracias!!! Me alegro que te haya gustado. Realmente, sólo he arañado la superficie. Pero creía interesante un artículo recopilatorio para por lo menos tener lo que yo llamo la «visión de conjunto». Después, será tu día a día como programador Javascript y las necesidades a las que te enfrentes las que decidan cuáles de estos puntos te son especialmente útiles («esto me suena de algo..») y entonces ya te animes a profundizar en ese punto concreto. Por ejemplo:

      • Las funciones callback para responder a las llamadas asíncronas, son casi fundamentales.
      • A mí me han sido muy útiles los arrays de funciones, para implementar mecanismos de suscripción a eventos definidos por mí (cuando ocurría el evento, recorría el array e invocaba a todas las funciones suscritas).
      • La IIFE son fundamentales para crear códigos aislados que no provoquen conflictos con otros códigos
  1. Buenos días,

    Muy buena aportación.

    ¿Puedes explicar un poco ésto?

    var multiplier = function(multiplier) { return function(number) { return multiplier * number; }; };
    console.debug(double(2)); // 4
    console.debug(multiplier(4)(2)); // 8

    Entiendo el caso de (4)(2) en el que se pasan parámetros a dos funciones. Supongo que (4) se pasa como parámetro multiplier, y 2 como number. ¿Es así o al revés?

    No entiendo que al pasar parámetros a una única función se copie a las dos.

    En mi opinión, tanta libertad a la hora de programar genera más problemas que beneficios… o es que vengo del Pascal (Delphi) 😉

    • Efectivamente, cuando ejecutas:

      multiplier(4)(2)

      – El valor 4 se pasa como parámetro multiplier
      – El valor 2 se pasa como parámetro number
      Primero, se invoca multiplier(4) y al resultado de esta invocación se la invoca pasándole el parámetro 2

      «No entiendo que al pasar parámetros a una única función se copie a las dos.»
      Creo que tienes que pensarlo del siguiente modo: estamos encadenando dos invocaciones seguidas (esto puedo hacerlo porque la primera función me devuelve un objeto function):
      – Paso 1: Javascript primero ejecuta multiplier(4) (no le presta atención -de momento- a lo que viene a continuación)
      – Paso 2: y al resultado de esta primera invocación le aplica (por fin) (2)

      Es como si lo hubiéramos hecho en dos pasos:

      // paso 1
      var nuevaFuncion = multiplier(4);
      // paso 2
      console.debug(nuevaFuncion(2)); // 8

      «En mi opinión, tanta libertad a la hora de programar genera más problemas que beneficios… o es que vengo del Pascal (Delphi)»
      Bueno, personalmente, siempre que he tenido la necesidad de aprender un lenguaje nuevo, creo que es fundamental dedicarle tiempo a estudiar sus peculiaridades, lo comparas con otros lenguajes que conoces (para ordenar tus pensamientos) y al final intentas aprovechar sus ventajas y convivir con sus inconvenientes (y te lo cuenta alguien que comenzó con la programación procedural y de ahí pasó a la programación orientada a objetos, por mi cuenta y riesgo)

      Suerte con Javascript 🙂

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

*
*
Sitio Web