26 agosto 2011

Javascript no es el demonio (creo)!

Hola a tod@s,

la verdad es que si hace 6 o 7 años alguien me hubiera dicho que estaría escribiendo un post sobre JavaScript que no fuera una crítica salvaje le hubiera tratado de loco. La verdad es que la idea que tenia en mi cabeza sobre JavaScript era la de un lenguaje de scripting para páginas HTML que, básicamente, les servía a los diseñadores gráficos para hacer “guarradas”  sobre el HTML y que convertía la interfaz de usuario en la parte mas oscura, inmantenible y “jodida” de modificar de las aplicaciones web. En la mayoría de aplicaciones acababas con cientos de funciones sueltas, spaggetti code con mucho JavaScript mezclado con el HTML, variables de ámbito global, inexistencia de tests unitarios, reutilización nula o prácticamente nula => Mi máxima siempre había sido: cuanto menos JavaScript tengamos que poner mejor porqué JavaScript = PROBLEMAS!

A todo esto cabe añadir también que nunca he sido muy amante del desarrollo de interfaces gráficas, básicamente por mi prácticamente nula capacidad artística (si, si, ahora ya se que no queda muy bien un título verde sobre un fondo rojo!) y siempre intentaba que fuera el equipo de UI el que lidiara con el JavaScript y enfrentarme a el lo mínimo posible.

A raíz de tener que implementar la UI en un proyecto MVC3 y con el objetivo de conseguir una interfaz agradable y, sobre todo, usable decidí usar JQuery (en MVC3 ya lo tenemos por defecto) para realizar validaciones en cliente, actualizaciones asíncronas de la interfaz y manipulaciones del DOM. Esto combinado con las capacidades de HTML5 y CSS3 (esto último mas para ver una pincelada de las “nuevas” tendencias en interfaces de usuario para aplicaciones web que por necesidad) me permitirían conseguir una UI agradable, potente y actual para mi aplicación web.

La primera decisión pues fue usar JQuery como framework para realizar la validaciones en cliente, gestionar las peticiones AJAX e interactuar con el DOM de las páginas HTML. Primera reflexión: JQuery es un framework de JavaScript => SHIT, me toca lidiar con el dichoso JavaScript!

Llegados a este punto tenia dos opciones:

  • Pasar de puntillas, es decir, coger un manual de JQuery, aprender las capacidades que proporciona para hacer las cuatro cosas que requería mi proyecto, cerrar los ojos e intentar no “enguarrar” demasiado mi UI.
  • Coger el toro por los cuernos y, dado que JQuery es un framework JavaScript, aprender JavaScript para ver si es posible realizar una UI “limpia” y mantenible con él.

Dado que uno de los objetivos del proyecto es experimentar un poco con diferentes tecnologías decidí elegir la segunda opción y darle una oportunidad a JavaScript. 

En el post voy a enumerar las características que me han sorprendido (ya sea para bien o para mal) en esta primera toma de contacto con Mr. JavaScript y introducir brevemente conceptos que nos pueden ayudar a resolver los problemas de los que adolecen muchas UI’s basadas en JavaScript. No esperéis encontrar en él temas avanzados de JavaScript (de hecho el primer título que le había puesto era JavaScript for dummies) ni una guía completa de las diferentes características del lenguaje. La idea del post es que os sirva como índice introductorio, todos los conceptos se presentaran de forma muy concisa (en un párrafo) y, para algunos de ellos, intentaré escribir entradas en el blog para verlos con mas profundidad.

Entre los puntos candidatos a tener, a priori, su propia entrada tenemos:

  • Objetos, funciones, clausuras y ámbitos.
  • Prototipos.
  • Duck typing (#cerveza gratis para @eiximenis por la palabreja).
  • Patrones de diseño.
  • OO clásica frente al enfoque prototípico.
  • Alternativas a la resolución de la herencia clásica en un lenguaje basado en prototipos.
  • Testing, TDD y JavaScript.
  • Proyectos ASP.NET MVC3, ¿cómo gestionamos la parte de JavaScript?.

Si ya conocéis JavaScript y lo usáis de una forma “correcta” posiblemente veáis el post como un refrito de obviedades. En la red hay muchísima literatura sobre JavaScript, muchas de las características las introduciré con una frase y os remitiré a algún enlace para que podáis profundizar en el tema.

Antes de entrar en materia un último apunte: el post esta escrito des del punto de vista de un desarrollador de aplicaciones de servidor acostumbrado a trabajar con lenguajes OO puros fuertemente tipados como Java y C# y con poca experiencia en interfaces de usuario, lenguajes de scripting y lenguajes funcionales.

¿Qué es JavaScript?

Básicamente podemos ver JavaScript cómo un lenguaje de scripting orientado a objetos, basado en prototipos, con funciones como entidades de primera clase y con tipado débil.

A partir de esta definición ya podemos extraer unas pocas conclusiones y abrir bastantes “cajas de pandora!”:

  • JavaScript es un lenguaje orientado a objetos => Debe posibilitar muchas de las características que soportan la mayoría de lenguajes OO: encapsulación, herencia, polimorfismo, etc… Ummmm! Interesante! parece que podremos hacer algo mas que definir conjuntos inconexos de variables globales y funciones!
  • JavaScript es un lenguaje basado en prototipos => En el lenguaje NO existe el concepto de clase sino el de prototipo: pero, perdonad la ignorancia de un triste programador Java/C#, ¿cómo podemos crear objetos con determinados atributos y propiedades sin definir la clase de la que será instancia? pues lo haremos en base al prototipo que todo objeto tiene!  Y este prototipo además nos permitirá hacer herencia por delegación y, por consiguiente, reutilizar código.
  • Las funciones son entidades de primera clase => Vale, bien, ¿y? ¿esto que implica? Pues implica que las funciones se tratan como cualquier otro tipo: se pueden asignar a variables, se pueden pasar como parámetros a métodos, ser devueltos por métodos, etc… ¿Os suena el concepto de puntero a función de C o de delegate en C#? Pues las funciones de JavaScript pueden funcionar de la misma manera. Además, al ser las funciones entidades de primera categoría nos lleva a pensar en JavaScript como un lenguaje candidato a usar una aproximación funcional para el desarrollo. 
  • JavaScript es un lenguaje de tipado débil => Ufff! Ya está, ya se pq no me gustaba JavaScript, lo de poder asignar peras a gatos y que no me avise el compilador de que la pera no puede maullar no me va mucho! Pero, un momento, si desarrollamos usando un enfoque ágil y una metodología orientada al test tendríamos que tener una prueba (fallida por supuesto) en la que le pidiéramos a la pera que maullara… Ummmm otra vez! Igual el hecho de seguir un enfoque dirigido al test hace menos importante las verificaciones de tipos en tiempo de compilación.

Funciones como entidades de primera clase…

El hecho de que las funciones sean entidades de primera clase permite:

  • Asignar funciones a variables.
  • Pasar funciones como argumentos de un método.
  • Devolver funciones como tipo de retorno de un método.
  • Desconectar la definición de la función de su contexto de ejecución y esto, junto con el concepto de clausura, nos ofrecerá unas capacidades importantes.
  • Usar una aproximación de lenguaje funcional al desarrollo.

Ya veremos que el hecho de que las funciones sean entidades de primer nivel nos proporcionará numerosas ventajas y nos permitirá usar una aproximación muy cercana a la programación en lenguajes funcionales aprovechando toda la potencia de estos.

¿Dónde están las variables locales? ¿y la encapsulación, reutilización, mantenibilidad y testabilidad de mi código?

En JavaScript solo tenemos dos ámbitos, el global y el local a una función => toda variable que no esté declarada con la palabra reservada var es global. Esto realmente es una deficiencia del lenguaje ya que nos induce a una mala práctica de programación como son las variables globales (creo que en eso estaremos todos de acuerdo!). Bueno, la primera en la frente! El problema de JavaScript no es que permita el uso de variables globales (casi todos los lenguajes las permiten, public static … en java por ejemplo) sino que JavaScript prácticamente obliga al uso de variables globales. Otro fallo relacionado con las variables globales es que cualquier variable que no se defina con var automáticamente es una variable global (con todos los errores y problemas que esto puede comportar). JavaScript tampoco proporciona ámbito de bloque y esto también nos puede llevar a errores difíciles de detectar.

La parte buena es que, aunque el lenguaje nos empuje a la definición de variables globales, existen técnicas y patrones para minimizar o eliminar el uso de variables globales en nuestros desarrollos, encapsular funcionalidades, ocultar miembros privados y proporcionar una alto grado de reutilización, mantenibilidad y testabilidad de nuestro código JavaScript.

En Techniques, Strategies and Patterns for Structuring JavaScript Code tenéis tres patrones de estructuración de código que os permitirán alcanzar los objetivos anteriores, fijaros como se puede pasar de una aplicación “spaggetti code” a una aplicación bastante estructurada, legible, mantenible y testeable simplemente con la aplicación de uno de los patrones indicados.

¿Y mis clases?

Como ya hemos comentado antes JavaScript es un lenguaje basado en prototipos, en un lenguaje de programación orientado a prototipos NO existen las clases y la herencia se basa en la clonación/copia de objetos existentes y la extensión de sus funcionalidades extendiendo el prototipo del objeto clonado/copiado.

Podéis obtener más información sobre la programación basada en prototipos comparada con la programación tradicional basada en clases en la wikipedia: Programación basada en prototipos y en este artículo de Pedro Cuesta (Universidad de Vigo). Encuentro especialmente interesante la reflexión sobre cómo influye este hecho en el enfoque del modelo de desarrollo que adoptamos, incitando a un desarrollo basado en ejemplos (¿os suena de algo?… metodologías ágiles, TDD, etc…) en vez de crear primero las abstracciones.

Nota: fijaros que el debate entre prototipos y clases es muy antiguo, la mayor parte de referencias son del siglo XX ;-)

Ostras, y puedo añadir comportamiento a los objetos dinámicamente!

Precisamente una de las características presentes en muchos lenguajes de programación basados en prototipos es la capacidad de extender dinámicamente el comportamiento de los objetos en tiempo de ejecución: dado que los objetos se crean por un proceso de copia es fácil modificar en tiempo de ejecución el comportamiento y la estructura de los objetos individuales. JavaScript es un lenguaje dinámico.

Esta muy chulo, si señor! Pero, ¿sirve de algo añadir comportamiento dinámico a nuestros objetos? Bueno pues esto nos lleva a la eterna discusión entre lenguajes dinámicos versus estáticos. No es mi intención posicionarme en este ni en un sentido ni en otro, tenéis mucha literatura en la web, lo que si parece claro es que los lenguajes estáticos cada vez están incorporando mas características de los dinámicos. Por ejemplo, .NET 4.0 incorpora los tipos dinámicos aunque sea básicamente para interoperar con lenguajes dinámicos (tenéis un buen artículo sobre Objetos dinámicos en .NET 4.0: ExpandoObject en el blog de José Manuel Alarcon)

Yo se muchas cosas de OO, ¿las puedo aplicar en JavaScript?

JavaScript es un lenguaje orientado a objetos, y yo estoy harto de trabajar con lenguajes orientados a objeto como Java y C#, entonces debo poder aplicar las buenas prácticas de programación OO en JavaScript, ¿verdad? Bueno pues la respuesta creo que es “si puedes, con un poco de esfuerzo, pero la pregunta realmente interesante es: ¿debo aplicar estas buenas prácticas tal y como lo haría en un lenguaje de programación basado en clases?

Pensad que JavaScript proporciona un modelo de herencia basado en prototipos muy diferente al que proporcionan Java y C# y, posiblemente, se puedan obtener los mismos beneficios pero la forma de hacerlo puede que sea diferente y mucho mas natural para un lenguaje basado en prototipos.

Tenéis un artículo buenísimo, Transitioning from Java Classes to JavaScript Prototypes, donde hay un ejemplo paso a paso de como implementar el patrón Observer en Java y en JavaScript. En él podréis ver que, efectivamente, se puede implementar este patrón tal y como lo haríamos en Java (o en C#) pero que resulta mucho más efectivo y elegante usar una aproximación que se aproveche del hecho que en JavaScript las funciones son entidades de primer nivel.

Oye, lo estas pintando todo muy de color de rosa, ¿es que no hay cosas mal resueltas en JavaScript?

Evidentemente, y bastantes, por enumerar solo algunas:

  • Dependencia de las variables de ámbito global.
  • Inexistencia del ámbito de bloque.
  • Sintaxis muy enrevesada para hacer cosas muy simples (leed JavaScript is Dead. Long Live JavaScript! para ver algunas de las propuestas de mejora de la sintaxis).
  • Uso del operador + tanto para añadir como para concatenar.
  • with y eval.
  • false, null, undefined, NaN.
  • etc…

Entonces, ¿recomiendas el uso de JavaScript?

En este post solo hemos visto unas breves pinceladas de JavaScript y con esto no podemos evaluar si es una buena opción o no, dependerá de mil factores: tipo de proyecto, tecnologías, alcance, equipo, etc…

Hay un hecho importante a tener muy en cuenta y que ha sido el que me ha hecho decantar por el uso de JavaScript en mi proyecto:  es la única opción si queremos programación en cliente para las aplicaciones web! Si os encontráis en este caso y tenéis que usarlo lo que si os recomiendo es que invirtáis algo de tiempo en entender la idiosincrasia del lenguaje e intentéis adaptaros a esta en vez de intentar que el lenguaje se adapte a vuestra manera de trabajar.

JavaScript tiene cosas malas pero también muchas cosas buenas, evitad las malas prácticas y centraros en aprovechar las bondades del lenguaje, que, aunque a estas alturas todavía me estén sorprendiendo, son bastantes. Y, sobre todo, intentad conocerlo antes de usarlo (como dice Douglas Crockford, JavaScript es de los pocos lenguajes que la gente cree que puede usar sin haberlo estudiado)!

Bien y con esta última recomendación lo dejamos por hoy, espero haberos abierto el apetito (o no haberos aburrido en exceso) y que leáis los siguientes artículos donde intentaré profundizar un poco más en algunos de estos temas. Podéis dejar cualquier sugerencia sobre temas de los que os gustaría hablar en los comentarios del post.

Saludos a tod@s! Josep Maria

Otras referencias

6 comentarios:

Mon dijo...

Me ha gustado... sobre todo el concepto de no querer que se adapte el lenguaje a ti, sino fluir con el lenguaje a través de sus características y adaptarte a él.

Ya no programo, hoy en día, mucho pero siempre que me hablan de Javascript tiendo a ser rencoroso en las soluciones que adoptan un uso intensivo de Javascript que no esta soportado por una libreria de mayor nivel que enmascare parte de su uso.

Yo voy más lejos en todo esto.. Porque hacer aplicaciones web cuando a veces no es necesario el subyacente web para muchas aplicaciones de entorno solamente empresarial... ?
Porque todo el mundo quiere web cuando a veces no genera ninguna ventaja y solamente añade complejidad.
Android, por ejemplo, un SO de nueva generación ya no se plantea los programans pensando en la web... sino que vuelva a los programa nativos utilizando las formas mas nativa evitando esta capa superflua y que por concepción no es dinámica como la web.

Como sigues en la brecha.

Un abrazo.

Jordi Pradel dijo...

Woo hoo! Javascript rules!

Javasciprt no solo no es el diablo, si no que es tu amigo. De entrada ha pasado a ser de los lenguajes mas multiplataforma que conozco.
Pero además, como lenguaje, es, como dices, bastante elegante.

A las críticas le añadiría que, como plataforma, apesta. Aunque se puede usar en mil plataformas, lo único que tienen en común es el propio lenguaje. Nada más. ¿Cómo importo una librería para usar en mi proyecto? Pues depende: En un navegador, la tienes que importar en el HTML. No puedes hacerlo desde el propio javascript. That sucks!

Pero quitaría lo que comentas de NaN, null, undefined y false. Para mí, en eso es muy elegante.

Sobre otras cosas mal resueltas te diría:
- A variables de ámbito global, bueno es el module pattern. En realidad, en muchos aspectos, mejor que la visibilidad privada y de package de la OO clásica.
- A inexistencia de ámbito de bloque bueno es el de función. Aunque la sintaxis C lía, haciéndonos creer que tendremos ámbito de bloque. No. No está.
- ¿Sintaxis enrevesada? No sé, sigo programando a menudo en un lenguaje donde hay que escribir getters y setters.
- with y eval: Ok. NO LOS USES.
- Operador +: No es tan complicado

Sobre recomendarlo o no... Si vas a hacer una aplicación web tendrás que usarlo. Y a menudo me pregunto... ¿de verdad tengo que generar todo ese html y elementos de presentación en el servidor para después manipularlo y tunearlo en el cliente? ¿no tendría más sentido generar datos en el servidor y la capa de presentación enterita en el cliente? Para según qué aplicaciones tendría todo el sentido del mundo.

En cualquier caso... Bon article, Xampi!!

Sergio León dijo...

"JavaScript es de los pocos lenguajes que la gente cree que puede usar sin haberlo estudiado"
Tiene más razón que un santo, buen post!

xampi dijo...

@Sergio Gracias!

@Mon Es lo que comentaba al final del post, usar o no JavaScript dependerá de un buen número de factores pero si tienes una interfaz web no tienes mas remedio. El hecho de la necesidad de una interfaz web para tu aplicación es un tema complejo que, como casi todo en esta vida, dependerá de cada caso particular... Gracias por el comentario!

xampi dijo...

@Jordi Pradel El post surge como primera toma de contacto, a vista de pájaro, con el demonio. A medida que vaya profundizando su uso iré publicando posts mas cortitos sobre temas concretos.

Profundizar un poco en las críticas y bondades del lenguaje una a una es uno de los primeros candidatos, tomo nota de tus referencias sobre el tema del false, null, etc... para analizarlo con calma y ver "que me he perdido" pq a mi me ha parecido un coñazo (ya te digo, posiblemente por desconocimiento).

Gràcies pel comentari Jordi, a cuidar-se!

Anónimo dijo...

Traveling by plane in the midst of any major holiday is expected to be heavy. [url=http://www.mulberryhandbagssale.co.uk]Mulberry ooutlet online[/url] say goodbye for good. [url=http://www.goosecoatsale.ca]canada goose online[/url] Llrwwguae
http://www.pandorajewelryvip.co.uk Iomfstjdw [url=http://www.officialcanadagooseparkae.com]canada goose chilliwack[/url] rnoxkjyls