Profundizando en Javascript, parte 1: funciones para todo

Javascript es un lenguaje que todos los programadores web creemos dominar. Da igual si programas en Php, Java o .Net: antes o después has tenido que hacer algo con Javascript. Y encima los que programamos en Java tenemos una pequeña ventaja añadida: que Javascript y Java comparten una sintaxix extremadamente parecida, lo cual lo hace todavía más sencillo, aparentemente, claro, porque solo se parecen en la sintaxis y nada más.

Así que dado el poco alcance (también aparentemente) que tiene Javascript, limitado normalmente al navegador (aunque hay excepciones), es poco usual profundizar en este lenguaje. Digamos que no merece la pena invertir tiempo en conocerlo a fondo, si lo comparamos con la utilidad que tiene profundizar en el propio Java, Hibernate, SQL o cualquier otro lenguaje/framework que usemos para nuestra aplicación web. Y encima ahora mucho más, ya que en estos últimos años han aparecido frameworks como jQuery, Prototype, Mootools o Mochikit que nos permiten, con mínimos conocimientos de Javacript, modificar el DOM a nuestro antojo, usar Ajax, hacer efectos y controlar eventos de manera no intrusiva sin importar el navegador en el que se visualice nuestra aplicación.

Sin embargo, este lenguaje tan “sencillo y facilón” esconde algo: no hay más que ver el código fuente de jQuery, por ejemplo, para darnos cuenta de que, simplemente, no se entiende tan facilmente como se debería: es como si el Javascript que conocemos de toda la vida se transformara en algo tremendamente distinto.

Un pelín de jQuery

Veamos, por ejemplo, que esconde el fichero jquery.js de la version 1.4.4 (podeis bajarlo desde aquí):

Para empezar, toda la librería, al completo, se engloba en este código:

(function( window, undefined ) {

	// Use the correct document accordingly with window argument (sandbox)
	var document = window.document;
	var jQuery = (function() {

		// Aquí va todo el código de jQuery

	});

})(window);

Vaya, es algo un poco, como lo diría, extravagante, ¿no? Intrigado, me puse a buscar que era toda esta suerte de funciones sin nombre que se ejecutan; y he encontrado cosas bastantes interesantes que me han hecho pensar que llevo toda la vida subestimando a este lenguaje y usándolo sin conocerlo del todo bien.

Una sola palabra clave para todo: function

Para empezar, Javascript es un lenguaje dinámico, interpretado, con closures, y que permite programación orientada a objetos (aunque no como la conocemos en Java, así que olvidate de herencia, interfaces, super, etc). Sin embargo, no tiene sentencias específicas para ello: no hay manera clara de definir un closure, ni un método, ni una clase, ni un objeto, ya que todos se hacen con la misma palabra clave: function.

Así que depende de como usemos “function” estamos definiendo una simple función, un closure, una clase o un método de una clase, ¡y todo con la misma palabra clave! Por supuesto, la palabra clave “this” tiene un significado más amplio, e incluso variable en función del contexto en el que se use. Para rematar, hay maneras distintas y equivalentes de programar orientado a objetos.

Pero empecemos por el principo. Esto es una función: la definición de un bloque de código que se ejecutará más adelante. A esta forma de crear funciones se llama función como declaración:

    function saluda(quien) {
        alert("hola "+quien)
    }

Esto por si solo no hace absolutamente nada, sino que es necesario invocarlo con una llamada a nuestra función

    saluda("mundo")

Las funciones son datos

Las funciones son funciones, pero tambien datos: el hecho de definir una función con su propio nombre (en este caso “saluda” es el nombre de la función) hace que se cree una variable global llamada “saluda” que podemos leer y pasar como parámetro, por ejemplo:

    function saluda() {
        alert("hola")
    }
    function ejecuta(func) {
        func()
    }
    ejecuta(saluda)

En este ejemplo, ejecutamos la función “ejecuta” pasándole como parámetro la función “saluda” como un dato, que pasa a llamarse “func” dentro de la función ejecuta, donde es invocada simplemente con func().

Tambien podemos crear funciones como expresiones de esta manera (además, es la recomendada, luego veremos porqué):

    var saluda = function(quien) {
        alert("hola "+quien)
    }
    saluda("mundo")

Con esta sintaxis lo que estamos haciendo en realidad es crear un función anónima y asignarle un nombre inmediatamente, por lo que es equivalente a definir la función como hacíamos al principio con function saluda(quien).

Para acabar, podemos crear funciones como expresiones y con nombre a la vez, como las funciones como declaración. La utilidad de esto es hacer funciones recursivas:

    var f = function fact(x) {
        if (x <= 1) return 1;
        else return x*fact(x-1); };

Dado que f es una variable externa y ajena a la función que puede cambiar en cualquier momento, darle el nombre “fact” a la función durante su propia definición es la única manera que tiene la propia función de llamarse a sí misma.

Funciones anónimas, autoejecutables y que devuelven funciones

Una función anónima se puede definir sin que sea asiganada a ninguna variable:

function(quien) {
	alert("hola "+quien)
}

Sin embargo, hacer esto es completamente inútil: definir una función sin nombre hace que sea imposible ser ejecutada más tarde, pues sin un nombre con el que acceder a ella es imposible encontrarla.
Pero podemos ejecutarla en el mismo momento en el que la definimos. Para ello, solo tenemos que encerrar entre paréntesis, y después usar unos nuevos paréntesis con los parámetros, como hacemos con una función normal.

	(function() { alert("hola mundo") })()

Por supuesto, podemos pasarle parámetros a nuestra función autoejecutable. En el siguiente ejemplo, se pasa como parámetro “mundo” a la función:

	(function(quien) {
		alert("hola "+quien)
	})("mundo")

Puede parecer poco útil hacer esto ahora, pero más adelante veremos como es una fantástica manera de arreglar ciertos problemas.

Por supuesto, una función puede devolver una función anónima. Sera responsabilidad del programador asignarla a una variable:

    function saludator(quien) {
        return function() {
            alert("hola "+quien)
        }
    }
    var saluda = saludator("mundo")
    saluda()

O podemos ejecutar la función que se ha retornado directamente, sin asignarla a ninguna variable:

    function saludator(quien) {
        return function() {
            alert("hola "+quien)
        }
    }

    saludator("mundo")()

Claro, nadie te impide sobreescribir una función con otra.

    function saludator(quien) {
        return function() {
            alert("hola "+quien)
        }
    }

    saludator = saludator("mundo")
    saludator()

Aquí, la primera vez que ejecutamos saludator(“mundo”) nos retorna una función anónima (que muestra “hola mundo”). Esta función es asignada a la variable saludator, por lo que la segunda vez que llamemos saludator(), estamos ejecutando la nueva función anónima (la del “hola mundo”), y la función inicial original se pierde para siempre.

Funciones dentro de funciones

Sigamos. Las funciones además se pueden anidar:

    function saluda(quien) {
        function alertasaludo(quien) {
            alert("hola "+quien)
        }
        alertasaludo(quien)

    }
    saluda("mundo")

Las funciones anidadas se llaman inner-private function. Inner porque son internas, y private porque son solo accesibles desde el código de la función desde donde son definidas. En nuestro ejemplo, alertasaludo() solo se puede invocar desde dentro de saluda().

Podemos combinar funciones anidadas con funciones que retornan funciones:

    function saludator(quien) {
        function alertasaludo() {
            alert("hola "+quien)
        }

        return alertasaludo
    }

    var saluda = saludator("mundo")
    saluda()

Y lo anterior lo podemos combinar con una función anónima auto-ejecutable.

    var saluda = (function(quien) {
        function alertasaludo() {
            alert("hola "+quien)
        }

        return alertasaludo
    })("mundo")

    saluda()

¿Y esto para que puede servir? Un ejemplo es el siguiente:

    var parOimpar = (function() {
        var hoy = new Date()
        if (new Date().getDate() % 2 == 0) {
            return function() { alert("hoy es dia par") }
        } else {
            return function() { alert("hoy es dia impar") }
        }
    })()

    parOimpar()

Con esto tenemos una función anónimoa autoejecutable que, evidentemente, solo se ejecutará una vez, con el único fin de crear una nueva función que nos muestre si el dia de hoy es par o impar.

Resumen

Llegados a este punto ya tenemos que tener claro varias cosas sobre las funciones:

– Que pueden tener nombre o no. Si no tienen nombre, son funciones anónimas.
– La dos sintaxis válidas para definir una función con nombre son:

   function nombre() {}
   var nombre = function() {}

– Que una función es tambien un dato y se puede manipular:
– Puedes asignar una función a una variable (y pasarla como parámetro a otra función)

         function yo() { alert("yo") }
         function saluda(alguien) { alguien() }
         saluda(yo)

– Puedes cambiar una función por otra, tan solo tienes que asignarle otra función al nombre anterior.

	    function yo() { alert("yo") }
	    function tu() { alert("tu") }
	    yo = tu

– Una función anónima puede ser ejecutada inmediatamente:

   (function() { alert("hola mundo") })()

– Las funciones se pueden anidar

   function quien() {
       function tu() { alert("tu") }
       tu()
   }

– Una función puede devolver otra función:

   function quien() {
   	   return function() { alert("yo") }
   }

Y con esto es todo por hoy. Todavía no tenemos el suficiente material para explicar porqué jQuery define todo la librería como una función anónima autoejecutable, pero al menos sabemos que lo hace y conocemos la sintáxis para hacerlo.
El próximo artículo veremos closures y programación orientada a objetos con Javascript, para acercarnos más y más a como funcionan jQuery y la mayoría de frameworks actuales por dentro. ¡Hasta entonces!

Más información:

16 thoughts on “Profundizando en Javascript, parte 1: funciones para todo

  1. En el primero de los trozos de código, es alert(“hola “+quien) no alert(“hola “+mundo).

    En los dos ejemplos posteriores a esta línea:
    “Podemos combinar funciones anidadas con funciones que retornan funciones:”
    sobran los () en “return alertasaludo()”. Este es un error muy gordo, eh! ;)

  2. Tienes toda la razón, ya lo he corregido! el primero es un error muy tonto, pero el segundo es peligroso: se devolvía el resultado de la invocación a la función, en vez de la propia función. Gracias!

  3. Muy amazing Alberto. Voy a seguir esta serie con especial interés, para pasar del estado de ‘Yo se javaScript porque valido formularios como Hulk Hogan reparte mamporros’ a ‘Sólo se que no se nada’ :)

  4. Muy bueno !!! Me lo guardo en los bookmarks. Lo encuentro muy instructivo. Las funciones anónimas en javascript és uno de los aspectos que más cuesten entender (sobre todo a los que no solemos utilizar funciones lambda, closures, y cosas por el estilo), pero lo cierto es que con la proliferación de frameworks tipo jQuery se hace muy necesario entender cómo funcionan.

    Gracias por el artículo. Estaremos atentos a los próximos

  5. plagelao: conocía las koans, pero no las de javascript. Las he estado mirando pero no las he intentado resolver, las dejaré para más adelante a ver si es verdad que he aprendido algo :D
    David: nadie sabemos nada de Javascript en realidad :D
    Juan: me alegro que te haya gustado :)

  6. Muy bueno el tutorial, espero ansioso la continuación… Yo espero pasar del estado “Sólo se que no se nada” al estado “Puede que sepa algo , no me acuerdo, pero sé donde puedes mrarlo:-)

  7. Yo no soy partidario de utilizar named functions. En lugar de

    function foo() {}

    Es más seguro

    var foo = function() {}

    Precisamente los detalles vienen en el post de kangax que referencias al final.

    En cualquier caso, es un artículo interesante. Un saludo.

  8. Vaya! El propio Nacho Coloma comentando!! como molaa! :) Yo tampoco soy partidario de crear funciones como sentencias, es mejor como expresiones, de hecho pongo que es la recomendada pero que luego veríamos porqué, dejando la explicación para otro día :b Un saludo Nacho!

  9. Yo lo que sí pondría es un pequeño aviso de que la construcción:

    var nombre = function nombre() { … };

    puede dar problemas en algunos navegadores.

  10. Excelente tutorial… soy de los que arranco directamente a a usar jQuery y por lo tanto le cuesta crear sus propios elementos en javascript, pero entendiendo como viene este ultimo, podriamos olvidarnos o mejorar muchisisimos las potencialidades de cualquier framework.

    quedo a la espera del siguiente tuto.

    saludos

  11. Muy buen material Alberto!!

    Espero la segunda parte para ver el motivo que compensa la pérdida de legibilidad de algunas de esas construcciones.

    A la escucha!!

  12. He llegado a este post tiempo después de su publicación pero ha merecido la pena leerlo. Realmente interesante. Me siento otro “Hulk Hogan” usando JS, como comentaban anteriormente. Espero las segunda parte!

Comments are closed.