Programación funcional en JavaScript: Las funciones

22400318_3008cc30d1_b

Encontramos unas cajas en el desván del señor Klein.

Para nuestra sorpresa, las cajas eran capaces de transformar unos objetos en otros. Si en una caja insertaba arena de la playa, al instante obtenía una piedra dura y rojiza. Si insertaba en otra caja esta piedra, obtenía una pipeta de oro. Si mezclaba las dos cajas podía convertir mi tierra en oro.

¡Eran mágicas!

Al igual que en el cuento anterior, las funciones tienen algo de mágico dentro de todo sistema. Si como hemos dicho, para seguir un estilo funcional, necesitamos crear funciones puras y sin efectos colaterales, podemos asegurarnos que dado un objeto, podamos transformarlo siempre en el mismo objeto diferente sin saber cómo se ha hecho.

Después de haber hecho un repaso sobre qué es la programación funcional y cómo podemos aprovecharnos de ciertas funcionalidades de la orientación a objetos, hoy terminamos la introducción de esta serie hablando sobre el eje central de este paradigma: Las funciones.

En este post hablaremos de cómo se comportan las funciones en JavaScript y que funcionalidades especiales nos proporciona el lenguaje para que podamos sacarle un mayor provecho en el estilo funcional.

Nota: Recordaros que esta serie de post sobre programación funcional en JavaScript no habría sido posible sin la ayuda que me está aportando este grandísimo libro. Os lo recomiendo encarecidamente como recurso de cabecera.

Las funciones en JavaScript

Las funciones son la unidad de trabajo mínima en JavaScript. Todo gira alrededor de ellas y es complicado llegar a hacer algo si no es implementando e invocando funciones.

Una función se invoca en JavaScript cuando escribimos el nombre de una función seguido de (). De esta manera una función internamente puede devolver un valor (en este caso se denomina expresión) o devolver undefined  (y en este caso se denomina sentencia).

Debido a nuestra labor funcional, siempre vamos a intentar usarlas para que devuelvan un valor al igual que hacen las funciones en el mundo matemático. Las sentencias son más comunes en estilos imperativos.

Las funciones en JavaScript se definen como entidades de orden superior y como ciudadanos de primera clase (first-class citizen en inglés). Son dos conceptos que suelen ir de la mano y que explicaremos a continuación:

Ciudadanos de primera clase

Cuando nos referimos que un elemento de un lenguaje de programación cumple con esta máxima, queremos decir que es un elemento que soporta todas las operaciones generalmente disponibles para otras entidades importantes como puedan ser los objetos.

Como bien sabemos, nosotros podemos definir funciones en JavaScript de esta manera:

function add(a, b) {
    return a + b;
}

Pero también podemos asignar una función a una variable:

var add = function(a, b) {
    return a + b;
}
var add = (a, b) => a + b;

O podemos asignar una función a un método de un objeto:

var calc = {
    add: f(a, b) => a + b
};

También tenemos una práctica menos común de definir una función que es por medio de su constructor. Sin embargo, es una prueba de la naturaleza de primera clase de las funciones en JavaScript:

var add = new Function('a', 'b', 'return a + b');

En JavaScript, toda función es una instancia del tipo ‘Function’.

Entidades de orden superior

Por otro lado, se dice que un elemento es de orden superior cuando ese elemento puede ser pasado como parámetro de otra función o devuelta por medio de un ‘return’.

Si anteriormente decíamos que la definición de una función en JavaScript podía guardarse en una variable, tiene sentido pensar que podría cumplir con esta característica.

Si seguimos con el ejemplo anterior, podemos ver que en JavaScript se puede realizar este ejemplo perfectamente:

function applyOperation(a, b, opt)  {
    return opt(a, b);
}

applyOperation(2, 3, add);

Y este otro también:

function add (a) {
    return function (b) {
        return a + b;
    }
}
add(2)(3);

Formas de invocar una función

Parece algo trivial la forma en que una función puede invocarse, sin embargo, es importante conocer todas sus formas para explicar una de las funcionalidades que más polémicas genera el lenguaje: La naturaleza de ‘this’.

A diferencia de cómo nos pasa en otros lenguajes de programación, en JavaScript ‘this’ no siempre apunta al elementos que nosotros nos esperamos, no es algo intuitivo en JavaScript. ‘this’ apunta a un elemento diferente dependiendo el contexto en el que una función se invoque.

En JavaScript contamos con estas 3 formas de invocar una función. Veamos donde apunta ‘this’ en cada una de ella:

  • Como una función global:
function setInit() {
    this.init = 'Hello World';
}
setInit();

En este caso ‘this’ señala al objeto global o a undefined si hemos indicado el ‘strict mode’.

  • Como un método de un objeto:
const obj = {
    prop: 'Hello World',
    getProp: function () {
        return this.prop;
    }
}
obj.getProp();

En este caso ‘this’ está apuntando al propio objeto. Sin embargo este comportamiento cambia si hacemos lo siguiente:

const getProp = obj.getProp;
getProp();

En este caso hay que tener cuidado porque la función se encuentra ejecutando en otro contexto que no es el del objeto y si el del contexto global por lo que el resultado varía.

Un buen truco para saber dónde apunta ‘this’ es ejecutar esa función. El this siempre apunta al elemento a la izquierda del punto que se antepone a la función ejecutada. Si vemos los casos anteriores:

obj.getProp(); // apunta a obj porque está a la izquierda del punto
window.getProp(); // apunta a window (objeto global) porque está a la izquierda del punto
  • Como un constructor anteponiendo un ‘new’ a la llamada:
function Obj()  {
    this.prop = 'Hello World';
}
const obj = new Obj();

En este caso apunta a obj porque la palabra reservada ‘new’ se encarga de hacer referencia del ‘this’ a dicho objeto.

Como vemos, 3 formas diferentes que alteran el comportamiento de ‘this’. Usar el estilo funcional en JavaScript es una buena idea porque es un estilo que va a intentar huir de contar con estados que no hayan sido definidos por los parámetros o estados internos de una función, es decir intentando huir del ‘this’, intentando no tener efectos secundarios por estados externos a una función.

Si huimos de ‘this’, evitaremos un gran números de errores que se comenten en JavaScript.

Los ámbitos

Otro problema con el que contamos en JavaScript es el comportamiento ‘extraño’ que tienen los ámbitos en JavaScript. Un ámbito en un lenguaje de programación determina en qué partes del programa una entidad (objeto, variable, función) puede ser usada.

En JavaScript contamos con dos ámbito hasta ES5:

  • El ámbito global: yo puedo definir variables fuera de mis funciones que estas variables se encuentran disponibles internamente. Como el siguiente caso:
const x = 5;
function printX() {
    console.log(x);
}
printX(); // pinta 5

Esto nos supone serios problemas a la hora de programar. Poder acceder y manipular variables de ámbitos superiores provoca que tengamos muchos efectos secundarios. Puede llegar a ser difícil de comprobar como cambia el estado de nuestra aplicación si usamos este tipo de técnica.

Además, dentro de una aplicación conviven muchos tipos de scripts que si hacen uso de variables globales puede que se pisen las unas con las otras.

Lo más habitual es prescindir del ámbito global todo lo que podamos.

  • El ámbito función: dentro de una función podemos declarar variables que estas no pueden ser accedidas desde fuera de su ámbito. Por ejemplo:
function foo() {
    const x = 5;
}
console.log(x); // undefined.

El ámbito se mantiene interno a la función. Esto, como veremos, nos ayudará a guardar ciertos estados de nuestra aplicación seguros.

Y con un tercero desde ES6:

  • El ámbito de bloque: desde ES6 podemos declarar variables que solo son disponibles dentro de un if, while, for o switch. Esto podemos conseguirlo gracias a la palabra reservada ‘let’.

Es muy recomendable que en la medida de lo posible intentemos declarar las variables que queramos mantener privadas dentro de un bloque con ‘let’. Si no es asi y seguimos usando la palabra reservada ‘var’ seguiremos teniendo un fallo garrafal que nos ha costado muchos disgustos. Imaginemos el siguiente código:

var arr = [1, 2, 3, 4];
function processArr() {
    function multipleBy10(val) {
        i = 10;
        return val * i;
    }

    for (var i = 0; i < arr.length; i++) {
         arr[i] = multipleBy10(arr[i]);
    }

    return arr;
}
processArr(); // [10, 2, 3, 4];

El probleme en este algoritmo viene a un proceso que se llama ‘hoisting’. JavaScript cuando evalua una función, lo primero que hace es buscar todas las variables y funciones y rescribirlo. Ese var i = 0 que observamos en el for se transforma en esta forma:

var arr = [1, 2, 3, 4];
function processArr() {
    var i;

    function multipleBy10(val) {
        i = 10;
        return val * i;
    }

    for (i = 0; i < arr.length; i++) {
         arr[i] = multipleBy10(arr[i]);
    }

    return arr;
}
processArr(); // [10, 2, 3, 4];

Por culpa de esto el i = 10 que encontramos en ‘multipleBy10’ es la que se encuentra definida en el ámbito de la función. Con let no hubiera ocurrido esto, el resultado hubiese sido [10, 20, 30, 40] porque en ese caso i si era una variable de bloque y el i = 10 sería una variable definida en el ámbito global al no tener la palabra ‘var’ delante. 

Poder jugar con los ámbitos es algo importante en el estilo funcional en JavaScript. Al no contar con sintaxis específica para proteger elementos, jugar con los ámbitos nos va a permitir aislar estados a nuestro gusto o necesidad.

Son muy importantes para entender los closures que explicaremos a continuación.

Los closures

Los closures son funciones que manejan variables independientes. En otras palabras, la función definida en el closure ‘recuerda’ el entorno en el que se ha creado.

Hay un ejemplo que me gusta mucho para explicar este comportamiento y es la de implementar un contador:

function getCounter(init = 0) {
    var counter = init;

    return function () {
        return ++counter;
    }
}

const incCounter = getCounter();
incCounter(); // 1
incCounter(); // 2

Está muy chulo, porque la función que devuelve ‘getCounter’ y que guardamos en ‘incCounter’ está recordando el estado interno de su padre. Esto es lo que se llama closure.

Y esto nos es muy útil porque nos sirve para conseguir 3 importantes aspectos a la hora de programar:

  • Emular las variables privadas: hacer esto nos va ayudar a modularizar nuestras aplicaciones, pudiendo esconder o mostrar aquello que necesitemos. Una forma de un módulo sería la siguiente:
const module = (function () {
    return {
        foo,
        boo
    };
    
    function foo() {
        return _foo();
    }

    function boo() {
        return _boo();
    }

    function _foo() {
        return 'foo';
    }

    function _boo() {
        return 'boo';
    }
})();

module.foo() // 'foo'
module._foo() // error
module.boo() // 'boo'
module._boo() // error
  • Emular ámbito de bloque: Aunque con ‘let’ es un problema mitigado, lo closures nos podían ayudar a mantener un ámbito de bloque. Creando funciones que se comporten como los iteradores for o while por ejemplo, podemos hacer que se eviten ese tipo de fallos. Veremos este tipo de soluciones en próximos posts.
  • Hacer llamadas asíncronas: el patrón callback se basa mucho en esta técnica para ejecutar funciones y pasar valores internos. Es algo bueno de que las funciones se comporten como elementos de primer orden y ciudadanos de primera clase.

Conclusión

Como podemos comprobar, las funciones en JavaScript nos ofrecen unas posibilidades increíbles. Esto ha de ser bueno ya que muchos lenguajes de programación están intentando dar esta flexibilidad a su implementación de las funciones.

Una vez hemos creado un contexto y hemos explorado diferentes conceptos introductorios de JavaScript necesarios para el resto de serie, empezaremos a hablar en próximos post en cómo orientar nuestros desarrollos para que sean más funcionales.

Nos leemos 🙂

PD: Como está siendo habitual en esta serie, os dejo el mapa mental de los conceptos clave del post:

las-funciones-en-javascript

Imagen de portada | flickr

Anuncios

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s