Funciones autoejecutables en Javascript

14 Mar 2011

De un tiempo a esta parte, no cabe ninguna duda de que uno de los patrones de diseño Javascript más interesante, flexible y poderoso es el construído e iniciado a través de las llamadas funciones autoejecutables. Estas funciones tiene la cualidad de que una vez declaradas, se llaman a sí mismas para inicializarse pasando desde ese momento a estar disponibles para otras partes de la aplicación.

Este tipo de estructura pueden codificarse de varias formas, pero la más popular es la siguiente:

(function(){ /* ... */ })();

Douglas Crockford ya la mencionaba en su libro The Good Parts, pero no fue hasta que empezaron a utilizarla las principales librerías y frameworks, que su uso no se popularizó comenzando a formar parte de otros patrones más elaborados.

En este artículo, vamos a repasar su funcionamiento, ventajas, posibles alternativas y algunos de los patrones más importantes de los que forma parte.

La función mágica

Para analizar el funcionamiento de la función paso a paso, tenemos que comenzar por su estructura más básica:

( ... )()

El esquema es el mismo que cuando llamamos a una función normal utilizando el nombre con que se ha declarado.

function myFunction(){
  console.log( 'Hello World' );
}
 
myFunction(); // 'Hello World'

Los paréntesis exteriores, como en una operación matématica, ‘encierran’ el contenido. El segundo juego de paréntesis asume que el interior de los anteriores es una función y la ejecuta inmediatamente. En el caso anterior, para invocar myFunction nada más declararla, pordríamos utilizar:

(myFunction)();

Sin embargo, para flexibilizar la estructura, en lugar de hacer referencia a una función existente, construímos una anónima:

(function(){})();

Con el código anterior, conseguimos crear una función, ejecutarla y luego desecharla: realmente lo interesante es que enlazamos el objeto devuelto al contexto global, no la función en sí misma. Con esto, sus propiedades y métodos tienen acceso al resto de variables globales dándole una gran flexibilidad y alcance al esquema.

Argumentos de preconfiguración

Un aspecto interesante de esta estructura es que admite parámetros a través de su segundo juego de paréntesis. Esta cualidad, por ejemplo, ha sido explotada por librerías como jQuery para un mantener un control preciso del contexto en el que se ejecutan.

La idea es la siguiente:

(function( param1, param2 ){
  console.log( param1 ); // Hello World
})( 'Hello World' );

En el ejemplo, hemos pasado como parámetro una cadena que nuestra función recoge para operar con él más adelante. Visto así no parece demasiado útil pero vamos a investigar un poco para sacarle más partido. Retomemos el ejemplo de jQuery para examinar su estructura base:

(function( window, undefined ){
  // ... jQuery code ...
})( window );

Tras un primer vistazo puede quizá despistar un poco: pasamos un parámetro (un objeto) y recogemos dos. ¿Por qué capturamos el objeto window? ¿Qué sentido tiene el undefined como argumento?

Pasando window como argumento, guardamos una copia del objeto dentro del contexto de la función lo cual es interesante por razones de rendimiento: cada vez que se tenga que acceder a este objeto, al haber sido declarado de forma local, evita que el intérprete Javascript suba niveles hasta el contexto global.

Con la declaración de undefined, evitamos posteriores errores inesperados: con anterioridad al modo estricto de la especificación ECMASCript 5, las palabras reservadas del lenguaje podían reescribirse como si de variables normales se tratase:

undefined = 'Hello World';

Al pasar como argumento undefined, estamos precisamente reescribiendo dicha variable con el valor que recibe del argumento que, en este caso, es… undefined! Es por tanto una forma de garantizar el valor original de esta palabra reservada con una forma elegante de escribir:

undefined = undefined;

Como podemos comprobar, la cosa comienza a ponerse interesante…

Otras formas de implementar este tipo de funciones

Antes de ver más ejemplos de uso, vamos a repasar otras formas con las que el lenguaje Javascript permite implementar funciones autoejecutables:

// Classic
(function(){})();
 
// Crockford's favorite
(function(){}());
 
// Unary versions
+function(){}();
 
// Facebook version
!function(){}();
 
// Crazy version
!1%-+~function(){}();

La alternativa de Crockford tiene su lógica semántica: si estamos seguros de que la función que precede al último juego de paréntesis es tal, podemos invocarla inmediatamente tras su declaración. Los paréntesis exteriores hacen, como en el modelo clásico, de contenedor.

Las variaciones que recurren a un operador unario podemos considerarlas más exóticas (eufemismo de friquis) pero su función es finalmente la misma: centrar el ‘foco’ del intérprete en la función sobre la que operan para inmediatamente invocarla con el juego de paréntesis final.

Podemos incluso recurrir al constructor new para conseguir una sintaxis más limpia:

new function(){}
 
// If we want to pass arguments, we need the set of parens:
new function(){}( params )

Una vez que sabemos cómo implementar este tipo de funciones, podemos repasar más casos de uso.

Patrones que utilizan la función autoejecutable

De los patrones de diseño más usuales en Javascript que utilizan esta estructura, el más conocido es sin duda el que el ya citado Crockford denominó módulo.

Con este diseño, buscamos que la función invocada devuelva un objeto que represente la interfaz pública para un determinado bloque de código. Básicamente la idea es distinguir entre métodos públicos y privados buscando un comportamiento similar a las clases de aquellos lenguajes orientados a objetos más tradicionales.

Su esquema es el siguiente:

var myApp = (function(){
  var foo = 'Hello World';
  return{
    foo : foo
  }
})();
 
console.log( myApp.foo ); // Hello World

Además de este patrón, tendríamos otros como el Factory Pattern, el Singleton o el This Namespace Proxy de James Edwards que recurren a esta estructura para inicializar el contexto en que actúan. Para un análisis detallado de cada uno de ellos, tenemos a nuestra disposición el libro gratuíto de Addy Osmani (@addyosmani) “Essential Javascript & jQuery Design Patterns” descargable desde su página oficial y recientemente actualizado.

Clausuras y estados

Un error muy frecuente durante el desarrollo de aplicaciónes es cuando tratamos de pasar como argumento de una función el índice de una colección de elementos.

Pensemos que queremos crear un bookmarklet que, al pasar el ratón por encima de una imagen, nos indique en la consola de Firebug su número dentro de la estructura DOM de la página.

El código no parece muy complicado:

var imagesCollection = document.getElementsByTagName( 'img' );
for( var i = 0, l = imagesCollection.length; i < l; i++ ){
  imagesCollection[ i ].addEventListener( 'mouseover', function(e){
    e.preventDefault();
    console.log( 'Image number: ', i );
  }, 'false' );
}

Podemos copiar y pegar este código en nuestra consola dentro de una página cualquiera de Flickr para ver qué sucede.

Algo va mal; cada vez que pasamos el cursor del ratón sobre una imagen nos da el mismo número: el número total de ellas. Esto es así porque el valor de la variable ‘i’ se fija y, cuando es invocado en el cuerpo de la función, su valor corresponde al último asignado o, lo que es lo mismo, al valor alcanzado en el bucle y que se corresponde con el total de elementos.

Para conseguir el comportamiento deseador, podemos utilizar una variable intermedia que recoja el valor de ‘i’ en cada iteración y cuyo valor no se altere a cada vuelta. En este caso, la función autoejecutable funciona perfectamente gracias a la posibilidad de aceptar parámetros:

var imagesCollection = document.getElementsByTagName( 'img' );
for( var i = 0, l = imagesCollection.length; i < l; i++ ){
  (function( currentImage ){
    imagesCollection[ i ].addEventListener( 'mouseover', function(e){
      e.preventDefault();
      console.log( 'Image number: ', currentImage );
    }, 'false' );
  })( i );
}

Mucho mejor; la nueva variable currentImage almacena el valor de ‘i’ en cada una de las iteraciones fijándolo al cuerpo de la función por lo que no será alterado en las siguiente vuelta. En esta ocasión, el código funciona como se espera y nos devuelve el número correcto de la imagen dentro de la estructura DOM de la página.

Conclusión

Como hemos visto a lo largo del artículo, los beneficios de la función autoejecutable en Javascript son muchos. De entre ellos, el más importante es, sin duda, el que toman algunos de los patrones de diseño que hemos mencionado más arriba: crear un contexto específico para un bloque código concreto que permita interactuar con el entorno global de un aplicación y su posterior reutilización. El patrón módulo toma directamente esta idea separando en un objeto sus métodos privados de aquellos públicos que crean la interfaz del mismo.

Para saber más

Para aprender más sobre algunos de los conceptos que hemos tratados en este artículo, podemos continuar leyendo las siguientes recomendaciones:

Peter Michaux, JavaScript Namespancing
Peter Michaux, An Important Pair of Parens
Klaus Komenda, JavaScript Programming Patterns
Ben Alman, Immediately-Invoked Function Expression (IIFE)

Más:

{8} Comentarios.

  1. Mon

    Hola!
    Sigo tu blog desde hace bastante tiempo, considero muy interesantes los artículos (últimamente llevas una racha bastante buena: tests unitarios, jquery 1.5, modo estricto….).
    Muchas gracias por dedicarle tanto tiempo, y muchos ánimos!

    • Carlos Benítez

      Gracias!
      Comentarios como este son los que animan a seguir escribiendo y a buscar temas que puedan resultar interesantes para todos.

      Un saludo.

  2. cristian cena

    totalmente! muchos estamos detras de tu blog carlos, es importantísimo que sigas escribiendo. Felicitaciones.

  3. Franz

    Gracias es justo lo que estaba buscando =)

  4. Hector Zarco

    Creo que e encontrado un error aqui:

    (function( currentImage ){
        imagesCollection[ i ].addEventListener( 'mouseover', function(e){ //Here
          e.preventDefault();
          console.log( 'Image number: ', currentImage );
        }, 'false' );
      })( i );
    

    Esa linea no deberia ser asi?

    imagesCollection[ currentImage ].addEventListener( 'mouseover', function(e){ 
    

    Saludos!

    • Carlos Benítez

      Hola Hector;
      precisamente tu línea sería cometer un error muy común ya que siempre estaría haciéndose referencia al último elemento del array.

      La función autoejecutable anterior pasa como argumento el valor ‘i’ que es la referencia correcta al índice del array en cada iteración.

      Saludos!

  5. marcos

    Formidable entrada. Muchas gracias.

  6. Emiliano

    Muy buen aporte, se agradece.

    Saludos!

Deja un comentario

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