Ir al contenido principal

Profundizando en Javascript, parte 1

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 hayexcepciones), 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 jQueryPrototypeMootools 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 ) {
02
03    // Use the correct document accordingly with window argument (sandbox)
04    var document = window.document;
05    var jQuery = (function() {
06
07        // Aquí va todo el código de jQuery
08
09    });
10
11})(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:
1function saluda(quien) {
2    alert("hola "+quien)
3}
Esto por si solo no hace absolutamente nada, sino que es necesario invocarlo con una llamada a nuestra función
1saluda("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:
1function saluda() {
2    alert("hola")
3}
4function ejecuta(func) {
5    func()
6}
7ejecuta(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é):
1var saluda = function(quien) {
2    alert("hola "+quien)
3}
4saluda("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:
1var f = function fact(x) {
2    if (x <= 1) return 1;
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:
1function(quien) {
2    alert("hola "+quien)
3}
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:
1(function(quien) {
2    alert("hola "+quien)
3})("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:
1function saludator(quien) {
2    return function() {
3        alert("hola "+quien)
4    }
5}
6var saluda = saludator("mundo")
7saluda()
O podemos ejecutar la función que se ha retornado directamente, sin asignarla a ninguna variable:
1function saludator(quien) {
2    return function() {
3        alert("hola "+quien)
4    }
5}
6
7saludator("mundo")()
Claro, nadie te impide sobreescribir una función con otra.
1function saludator(quien) {
2    return function() {
3        alert("hola "+quien)
4    }
5}
6
7saludator = saludator("mundo")
8saludator()
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:
1function saluda(quien) {
2    function alertasaludo(quien) {
3        alert("hola "+quien)
4    }
5    alertasaludo(quien)
6
7}
8saluda("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:
01function saludator(quien) {
02    function alertasaludo() {
03        alert("hola "+quien)
04    }
05
06    return alertasaludo
07}
08
09var saluda = saludator("mundo")
10saluda()
Y lo anterior lo podemos combinar con una función anónima auto-ejecutable.
1var saluda = (function(quien) {
2    function alertasaludo() {
3        alert("hola "+quien)
4    }
5
6    return alertasaludo
7})("mundo")
8
9saluda()
¿Y esto para que puede servir? Un ejemplo es el siguiente:
01var parOimpar = (function() {
02    var hoy = new Date()
03    if (new Date().getDate() % 2 == 0) {
04        return function() { alert("hoy es dia par") }
05    else {
06        return function() { alert("hoy es dia impar") }
07    }
08})()
09
10parOimpar()
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:
1function nombre() {}
2var 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)
1function yo() { alert("yo") }
2function saluda(alguien) { alguien() }
3saluda(yo)
- Puedes cambiar una función por otra, tan solo tienes que asignarle otra función al nombre anterior.
1function yo() { alert("yo") }
2function tu() { alert("tu") }
3yo = tu
- Una función anónima puede ser ejecutada inmediatamente:
1(function() { alert("hola mundo") })()
- Las funciones se pueden anidar
1function quien() {
2    function tu() { alert("tu") }
3    tu()
4}
- Una función puede devolver otra función:
1function quien() {
2    return function() { alert("yo") }
3}
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

Entradas populares de este blog

Llenar un dropdownlist de mes con C#

Buenas, acá dejo un ejemplo de como llenar un dropdownlist. Lo publico como para tenerlo de referencia, tal vez a alguien le sirva también. Código en C# (code behind)        private void CargarMes()        {            //valor por default            ddlMes.Items.Add("Seleccione mes");            List<string> nombreMes = DateTimeFormatInfo.CurrentInfo.MonthNames.Take(12).ToList();            var listaMesesSeleccionados = nombreMes.Select(m => new            {                Id = nombreMes.IndexOf(m) + 1,                Name = m            });            foreach (var mes in listaMesesSeleccionados)            {  ...

Borrar carpetas .svn en Windows

Cuando se empieza a usar  Subversion  (SVN), para tener un control de los archivos de nuestro proyectos, en ocasiones usaremos clases, librerías, frameworks o carpetas de un repositorio de un tercero. Cuando unimos estas carpetas nuevas, a nuestro repositorio cliente, esta no los interpreta bien porque ya vienen con otras carpetas .SVN con archivos específicos para su repositorio de origen. La solución aquí es eliminar todas las carpetas .SVN de lo que queramos implementar para que nuestro repositorio cliente lo interprete como nuevos archivos y podamos agregarlas al proyecto. Con esto se eliminarán las carpetas .SVN de forma recursiva. Pero escribir toda esa línea no es muy cómodo que digamos en nuestro trabajo del día a día, por eso vamos a automatizar este proceso. En Windows Crearemos un archivo que llamaremos "borrar carpetas SVN.reg" y contendrá lo siguiente: Código : Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\Directory\shell\DeleteSVNFolders] ...

Recursividad

1.1.  Introducción . El concepto de recursividad va ligado al de repetición. Son recursivos aquellos algoritmos que, estando encapsulados dentro de una función, son llamados desde ella misma una y otra vez, en contraposición a los algoritmos iterativos, que hacen uso de bucles while, do-while, for, etc. 1.2.  Definición . Algo es recursivo si se define en términos de sí mismo (cuando para definirse hace mención a sí mismo). Para que una definición recursiva sea válida, la referencia a sí misma debe ser relativamente más sencilla que el caso considerado. 1.3. Elementos de la Recursión  1.3. 1.  Axioma  Es un caso donde el problema puede resolverse sin tener que hacer uso de una nueva llamada a sí mismo. Evita la continuación indefinida de las partes recursivas. 1.3.2.  Formula recursiva Relaciona el resultado del algoritmo con resultados de casos más simples. Se hacen nuevas llamadas a la función, pero están más próximas al caso base. Por eje...