Articulos
Contacto

Funciones en Javascript

Fundamentos Javascript (IV) - 7 formas de declarar una función en Javascript

Publicado el: 7 de Septiembre 2019 por Victor de Andrés Archivado en:
Javascript Functions
Photo by Samuel Zeller on Unsplash

Por definición una función es un procedimiento, un conjunto de sentencias que realizan una tarea o calculan un valor. En Javascript este es uno de los conceptos más importantes y versátiles de este lenguaje de programación.

Una de las características más importantes de las funciones en Javascript es su versatilidad. En Javascript las funciones son diferentes a las funciones de otros lenguajes de programación. En Javascript las funciones son un objeto. Por ello se les puede asignar variables, arrays u otros objetos. La forma en la cual hallamos definido nuestra función, definirá el comportamiento de la misma. Siendo los tipos de función que podemos crear en Javascript los siguientes:

  • Function declaration

  • Function expression

  • IIFE (Immediately Invoked Function Expression)

  • Shorthand method definition

  • Arrow function

  • Generator function

  • Function constructor

En este ocasión dejaré los títulos en inglés para un mejor reconocimiento posterior. Por si lees otros artículos y/o manuales y puedas distinguirlas fácilmente.

Function declaration.

Comenzaremos por la más sencilla de todas, la "Function declaration".

Este tipo de función se creará con la palabra reservada function, seguido obligatoriamente por un nombre, que identificará a nuestra función, una lista de parámetros entre paréntesis, y el símbolo de las llaves {}. Qué será el que delimite el contenido de nuestro conjunto de sentencias.

La lista de parámetros cuando creamos una función no es obligatoria, podemos definir una función sin parámetros.

Cuando estemos trabajando con funciones es importante que diferencies entre parámetro y argumento. Un parámetro es/son la/s variables que definimos cuando creamos la función. Y los argumentos son los datos que pasamos a la función cuando la invoquemos. Serán los valores de los parámetros de la función.

Una vez hemos visto cómo definir nuestra función en Javascript, realicemos un simple ejemplo.

      
  function hola(nombre){
    console.log(`Hola ${nombre}.`)
  }
     
  hola('Victor');    // => Hola Victor
      
    

En este ejemplo definimos nuestra función hola con un parámetro "nombre" y escribirá por consola hola y el nombre con el que hayamos invocado nuestra función.

Otra característica es que por defecto las funciones devuelven el valor "undefined". Si queremos que nuestra función devuelva algún valor debemos utilizar la instrucción return en nuestro bloque.

      
  function respuesta(){ }
  
  console.log(respuesta());    // => undefined
  
  function respuesta2(){
        return 'Hola';
  }
  
  console.log(respuesta2());   // => Hola
      
    

Además este tipo de funciones son compatibles con el hoisting. El hoisting es una característica de Javascript por la cual las definiciones se ejecutan al principio de la ejecución del código.

Si por ejemplo escribimos el siguiente código.

      
  hola('Victor');      // => Hola Victor

  function hola(nombre) {
      console.log(`Hola ${nombre}.`)
  }
      
    

No obtendremos error al invocar la función hola, que aún no ha sido creada, ya que Javascript lo habrá ejecutado de la siguiente forma:

      
  function hola(nombre){
    console.log(`Hola ${nombre}.`)
  }

  hola('Victor');    // => Hola Victor
      
    

Function expression.

La siguiente forma de declarar una nueva función que vamos a ver es muy similar al tipo de anterior, "Function Declaration". La sintaxis para crear estas funciones es similar a la anterior. La única diferencia es que la definición de nuestra nueva función no comienza por instrucción function y el nombre de la función es opcional.

Este tipo de funciones las podemos almacenar en una variable. Lo cual nos permite utilizarlas en algunos de los siguientes ejemplos.

      
  // Asignarla a una variable como un objeto
  const ejemplo = function(...) { ... }
  
  // Crear un método en un objecto
  {
      ...
      'suma': function(...) { ... },
      ...
  }
  
  // Utilizar la función como un callback
  .cb(function(...) { ... })
      
    

A continuación vamos a ver cómo crear una función que sumará dos al argumento que enviemos a la función.

      
  const SUMADOS = function sumaDos(valor) {
      return valor + 2;
  }
  
  console.log(SUMADOS(5));    // => 7
      
    

Si al crear nuestra nueva función no utilizamos un identificador estaremos creando una función anónima.

Veámoslo con el mismo ejemplo.

      
  const SUMADOS = function(valor) {
    return valor + 2;
  }
     
  console.log(SUMADOS(5));    // => 7
      
    

Como puedes ver el resultado es el mismo.

Un diferencia con las "Function Declaration" es que estas no son compatibles con el hoisting. Por ello si invocas una de estas funciones antes de su declaración antes de su definición obtendrás un error.

      
  hola('Victor')
  // ReferenceError: hola is not defined
  
  
  const hola = function hola(nombre) {
      console.log(`Hola ${nombre}.`)
  }        
      
    

IIFE (Immediately Invoked Function Expression)

Normalmente cuando definimos una función es porque la vamos a llamar en varias ocasiones, pero y sí sólo queremos llamar a la función en una sola ocasión y obtener un resultado. Para ello podemos utilizar la funciones IIFE. Este tipo de funciones se ejecutan inmediatamente y no son accesibles posteriormente.

Para crear una función de este tipo deberemos crearla en un operador de agrupación (). Seguido de (), lo que posibilitará el interpretado directamente en el motor de JavaScript.

Veámoslo con un pequeño ejemplo.

      
  ( function () {
      let nombre = 'Victor'
      return `Hola ${nombre}`
  })()
      
    

Al definir nuestra función dentro de un operador de agrupación no permitimos acceder a variables fuera del IIFE, así como no "contaminar" el scope global.

      
  (function () { 
      var nombre = "Victor";
  })();
  
  console.log(nombre);        // Uncaught ReferenceError: nombre is not defined
      
    

Shorthand method definition.

El cuarto tipo de definición que vamos a ver es el "Shorthand method". Este forma de crear las funciones puede ser utilizado como método en la declaración de un objeto o en las clases de ES6.

Para crear este tipo de función debemos asignar un nombre de función seguido de una lista de parámetros entre paréntesis y los símbolos de llaves para delimitar el cuerpo de las instrucciones.

Lo veremos mejor con un ejemplo.

      
  const poblacion = {
    personas: [],
    add(...personas) {
      this.personas.push(...personas);
    },
    saluda(index) {
      return `Hola soy ${this.personas[index]}`;
    }
  };
  
  poblacion.add('Luis', 'Jesus', 'Victor');
  poblacion.saluda(1) // => 'Hola soy Jesus'        
      
    

Veamos en detalle este ejemplo. Add() y saluda() son dos métodos de la clase poblacion que hemos definido con el método "Shorthand". A continuación hemos invocados los dos métodos de la clase. En primer lugar poblacion.add para añadir tres personas, Luis, Jesus y Victor. Y posteriormente hemos invocado al método saluda, enviando como parámetro 1 que nos devuelve un saludo de la persona número 1, en nuestro ejemplo Jesús.

Como puedes observar esta sintaxis es muy sencilla, si escribiéramos esta misma función de una manera clásica sería como a continuación.

      
  const poblacion = {
    personas: [],
    add: function(...personas) {
      this.personas.push(...personas);
    },
    saluda: function(index) {
      return `Hola soy ${this.personas[index]}`;
    }
  };
  
  poblacion.add('Luis', 'Jesus', 'Victor');
  poblacion.saluda(1); // => 'Hola soy Jesus'      
      
    

Arrow function.

Otra de las novedades de ES6 son las "Arrow function". La forma de crear estas funciones es la siguiente: Primero definiremos la lista de parámetros, en caso de ser necesario, entre paréntesis seguido del símbolo => y las {} para indicar las instrucciones que se van a realizar.

Además de la sintaxis que es diferente a las anteriores este tipo de funciones tienen las siguientes características.

  • Las "arrow function" no crean su propio contexto al ejecutarse. Al contrario que las "function expression" o las "function declaration" que crea su propio contexto.

  • Las "arrow function" son anónimas.

  • El objeto arguments no se encuentra en el contexto de la función.

  • Si al definir la función no utilizamos el símbolo de las llaves. La función devolverá como resultado de la función el resultado de la ejecución de la instrucción que hayamos indicado.

Como en las demás ocasiones vamos a verlo con un pequeño ejemplo.

      
  const saluda = (nombre) => {
    return `Hola ${nombre}`;
  }
  
  console.log(saluda('Victor'))   // => Hola Victor
  
  // Vamos a compactar un poco más el código.
  const saluda2 = (nombre) => `Hola ${nombre}`;
  
  console.log(saluda2('Victor'));  // => Hola Victor
      
    

En estos dos ejemplos hemos escrito una "arrow function" a la cuál le enviamos como parámetro un nombre y nos devuelve la cadena de texto "Hola <nombre>". Ambas funciones son iguales, pero en el segundo ejemplo podemos observar como las "arrow function" nos han devuelto un cadena de texto sin utilizar la instrucción return.

Generator function.

Hasta el momento todas las funciones que hemos visto se ejecutan completamente, y no se paran hasta la última línea de la función Si queremos salir con antelación de una función debemos ejecutar un return, o lanzar un error. Por lo que las instrucciones que se encuentren a continuación del return o el error no se ejecutarán.

Pero la característica más significativa de estas funciones es que nos permite parar o salir de la función en un punto dentro del conjunto de instrucciones que forman nuestra función y retornar posteriormente la ejecución desde el punto en el que paramos anteriormente.

La sintaxis de estas funciones es igual a las "function declaration" o "function expression". Sólo debemos utilizar el símbolo * al comienzo de la definición de nuestra función.

Otra diferencia de estas funciones es que siempre retorna un objeto con la siguiente estructura cada vez que invoquemos a la función next().

      
  {
    value: any,
    done: true|false
  }
      
    

Como puedes observar el objeto tiene dos propiedades. Values, qué es el valor actual del objeto y done que nos indica si la función a finalizado o no.

Pero ahora mismo te estarás preguntando cómo debo salir de la función y cómo vuelvo después a ella, y se sigue ejecutando desde el punto en que salí.

Veámoslo con un simple ejemplo donde lo explicaré detalladamente y será más sencillo comprender el funcionamiento de este tipo de funciones.

      
  function *generatorFunction()
  { 
    console.log('Ejemplo generator Function.');
    // yield. Salimos de la función.
    // La propiedad value es igual a 'Un kit kat'
    yield 'Un kit kat';  
    
    console.log(' ¿ Continuamos ?');  
    // yield. Volvemos a salir de la función.
    // La propiedad value es igual a 'Fin del ejemplo'
    yield 'Fin del ejemplo';
    
  }
  
  const gen = generatorFunction();
  
  console.log(gen.next().value);                    
  console.log('La función se encuentra pausada');        
  console.log(gen.next().value);
  console.log(gen.next().value);
  
  // El resultado de la ejecución sería el siguiente: 
  
  // => Ejemplo generator Function.
  // => Un kit kat
  // => La función se encuentra pausada
  // => ¿ Continuamos ?
  // => Fin del ejemplo
  // => undefined          
      
    

Lo primero que hemos realizado en este ejemplo ha sido crear la función generatorFunction. Posteriormente hemos asignado a la constante gen nuestra función. Una vez realizada la tarea de asignación de nuestra función a una variable la invocamos por primera vez.

      
  console.log(gen.next().value);         
      
    

De esta forma comienza la ejecución de nuestra función. Donde la primera instrucción muestra en la consola el mensaje "Ejemplo generator Function". La siguiente instrucción es:

      
  yield 'Un kit kat';  
      
    

La instrucción yield nos retorna un objeto, como ya vimos anteriormente. Cuyas propiedades son el texto que enviamos 'Un kit kat' en value, y false en la propiedad done.

Ahora ya estamos fuera de la función, podemos ejecutar las instrucciones que deseemos, mientras nuestra función se encuentra en pausa. En nuestro ejemplo mostramos por consola el mensaje "La función se encuentra pausada".

Una vez ejecutada(s) nuestra(s) instrucción(es) invocamos de nuevo a nuestra función que se encontraba en pausa.

      
  console.log(gen.next().value);
      
    

Que nos mostrará por consola el siguiente mensaje "¿ Continuamos ?". Y finalizará nuestra función con el siguiente mensaje por consola "Fin del ejemplo".

Function constructor.

Y finalmente la "Function constructor". Es una forma bastante inusual de declarar las funciones pero vamos a verla también. Tal vez no sea útil en algún momento.

En Javascript las funciones son objetos de "primera clase". Esto quiere decir que se pueden crear nuevas funciones durante la ejecución del programa. Veamos más en detalle cómo crear esta tipo de funciones.

Las funciones en Javascript, son funciones, y poseen el constructor Function. Por ello podremos crear una variable que invoque a un objeto Function. Cuando invoquemos a este objeto podremos enviar todos los argumentos que deseemos. Los primeros n argumentos serán los parámetros de nuestra función y el último argumento será el código de nuestra función.

Veámoslo en detalle con el siguiente ejemplo. Imaginad que necesitamos crear una función que sume dos valores en tiempo de ejecución.

      
  const sumaFunction = new Function('numero_1', 'numero_2', 
    'return numero_1 + numero_2'
  );        

  // Ejecutamos nuestra función.
  console.log(sumaFunction(10, 15)) // => 25
      
    

En este ejemplo hemos creado la constante sumaFunction que crea una nueva instancia del objeto Function al cual le enviamos tres argumentos. Los dos primeros argumentos, 'numero_1' y 'numero_2' serán los parámetros de nuestra nueva función y el tercer parámetro será el cuerpo de nuestra función.

Esta forma de definir esta función es equivalente a la siguiente:

      
  const sumaFunction = function(numero_1, numero_2) {
      return numero_1 + numero_2
  }
  
  // Ejecutamos nuestra función.
  console.log(sumaFunction(10, 15)) // => 25        
      
    

Conclusión.

En este artículo hemos visto 7 formas diferentes de definir funciones en Javascript. No podemos decir que hay una forma mejor que otras. En función de la situación en la que nos encontremos o el problema que queramos resolver será mejor utilizar unas u otra.

Espero que con este artículo conozcas un poco mejor la versatilidad y la potencia de las funciones en Javascript.

Comentarios
Escribe tu comentario