Articulo que me gusto mucho de Alberto Vilches
publicó en su blog
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:
01 | ( function ( window, undefined ) { |
04 | var document = window.document; |
05 | var jQuery = ( function () { |
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:
1 | function saluda(quien) { |
Esto por si solo no hace absolutamente nada, sino que es necesario invocarlo con una llamada a nuestra función
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:
4 | function ejecuta(func) { |
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é):
1 | var saluda = function (quien) { |
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:
1 | var f = function fact(x) { |
3 | 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:
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.
1 | ( 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:
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:
1 | function saludator(quien) { |
6 | var saluda = saludator( "mundo" ) |
O podemos ejecutar la función que se ha retornado directamente, sin asignarla a ninguna variable:
1 | function saludator(quien) { |
Claro, nadie te impide sobreescribir una función con otra.
1 | function saludator(quien) { |
7 | saludator = saludator( "mundo" ) |
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:
1 | function saluda(quien) { |
2 | function alertasaludo(quien) { |
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:
01 | function saludator(quien) { |
02 | function alertasaludo() { |
09 | var saluda = saludator( "mundo" ) |
Y lo anterior lo podemos combinar con una función anónima auto-ejecutable.
1 | var saluda = ( function (quien) { |
2 | function alertasaludo() { |
¿Y esto para que puede servir? Un ejemplo es el siguiente:
01 | var parOimpar = ( function () { |
03 | if ( new Date().getDate() % 2 == 0) { |
04 | return function () { alert( "hoy es dia par" ) } |
06 | return function () { alert( "hoy es dia impar" ) } |
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:
2 | 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)
1 | function yo() { alert( "yo" ) } |
2 | function saluda(alguien) { alguien() } |
- Puedes cambiar una función por otra, tan solo tienes que asignarle otra función al nombre anterior.
1 | function yo() { alert( "yo" ) } |
2 | function tu() { alert( "tu" ) } |
- Una función anónima puede ser ejecutada inmediatamente:
1 | ( function () { alert( "hola mundo" ) })() |
- Las funciones se pueden anidar
2 | function tu() { alert( "tu" ) } |
- Una función puede devolver otra función:
2 | 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:
Comentarios
Publicar un comentario