¿cómo mantener tu javascript?: buenas prácticas

Post on 05-Dec-2014

170 Views

Category:

Technology

5 Downloads

Preview:

Click to see full reader

DESCRIPTION

Buenas práctias en el desarrollo de software con javascript. Código limpio, mantenible, escalable, tests,... Charla perteneciente al evento Betabeers Murcia del día 9 de Mayo de 2014

TRANSCRIPT

COMO MANTENER TU JAVASCRIPT:

Humberto García Caballero

Buenas prácticas

CONTENIDOS1. Motivación.

2. Guía de estilos.

3. Prácticas de programación.

4. Automatización.

5. Conclusiones.

6. Preguntas.

7. Bibliografía.

– Douglas Crockford

“JavaScript: The world's most misunderstood programming language.”

MOTIVACIÓN

if (wl && wl.length) { for (i = 0, l = wl.length; i < l; ++i) { p = wl[i]; type = Y.Lang.type(r[p]); if (s.hasOwnProperty(p)) { if (merge && type == 'object') { ! Y.mix(r[p], s[p]); } else if (ov || !(p in r)) { r[p] = s[p]; } } } }

if (wl && wl.length) { ! for (i = 0, l = wl.length; i < l; ++i) { p = wl[i]; type = Y.Lang.type(r[p]); ! if (s.hasOwnProperty(p)) { if (merge && type == 'object') { Y.mix(r[p], s[p]); } else if (ov || !(p in r)) { r[p] = s[p]; } } } }

GUÍA DE ESTILOS¿Por qué? Mantener el mismo estilo de código en un equipo de desarrollo es fundamental por varios motivos. Cualquier miembro del equipo puede comprender fácilmente código escrito por otro miembro, permitiendo un desarrollo más eficiente y evitando tener que gastar tiempo en comprender un estilo de código diferente. Los errores son más fáciles de detectar.

Inicialmente el equipo debería mantener una reunión para decidir cuál será su estilo de código, ya que todos los miembros tendrán sus preferencias personales y todos intentarán mantenerlas. Hay que lograr un compromiso.

ESTILOS: HERRAMIENTAS

• JSLint: Desarrollado por Douglas Crockford, esta herramienta nos informa sobre errores potenciales hallados en nuestro código, así como advertencias sobre el uso de ciertos patrones de estilo no recomendados.

• JSHint: Es un derivado de JSLint mantenido por Anton Kovalyov. Ayuda a mantener las convecciones de código así como evitar errores en nuestro código JS. Mozilla, Facebook, Twitter, Bootstrap, jQuery,..., todos ellos utilizan esta herramienta, por lo que es una buena opción ya que su mantenimiento está prácticamente asegurado.

Incluir una herramienta de este tipo en nuestro proceso de construcción es una práctica muy recomendable, ya que forzamos a que se cumplan las convecciones de código, así como evitar errores potenciales (p.e. uso de eval).

ESTILOS: PRINCIPALES ELEMENTOS A TENER EN CUENTA• Identación: La primera decisión a tomar es el estilo de identación. Dos alternativas: tabs o

espacios. Normalmente 4 espacios.

• Terminación de sentencias: JS permite terminar lineas con y sin ; debido al ASI (Automatic Semicolon Insertion). La recomendación aquí es insertarlo siempre, así evitaremos que el código interpretado tenga un comportamiento totalmente diferente al deseado. (Ver ejemplo siguiente). JSLint y JSHint lanzan warnings en estos casos.

• Tamaño de línea: Estrechamente relacionado con la identación. La mayoría de convecciones indican 80 caracteres por línea (Android 100). Establecer un ancho máximo evita tener que hacer scroll horizontal cada vez que queremos modificar el código, mejorando la producción.

• Rotura de líneas: Cuando una línea alcanza el máximo de caractéres, ésta debe ser dividida en dos. La norma aquí suele ser partirlas después de un operador y que la siguiente línea tenga dos niveles de identación. (ver ejemplo).

• Líneas en blanco: La norma en este caso es introducir una línea en blanco para separar partes de código que no estén relacionadas, quedando el código como si fuesen párrafos en lugar de una secuencia de símbolos. Entre métodos, entre variables locales de un método y su primera sentencia, antes de un comentario, entre secciones lógicas de un método.

• Nombramiento: Se recomienda siempre utilizar el estilo propio que tenga el lenguaje de programación. En el caso de JS es camel case. Variables = nombres, funciones = verbos, objetos con capitalización.

• Constantes: En mayúsculas y con barra baja para los espacios.

// Código original function getEvento() { return { event: "Betabeers Murcia VI", place: "You&Co" } }

Ejemplo: Terminación de sentencias.

// Tras el ASI, quedaría así function getEvento() { return; { event: "Betabeers Murcia VI", place: "You&Co" }; }

Ejemplo: Rotura de líneas

// MAL: Sólo un nivel de identación. callAFunction(document, element, window, "some string value", true, 123, navigator);

// MAL: Línea partida antes de operador. callAFunction(document, element, window, "some string value", true, 123 , navigator);

// Bien: Después de operador seguido de dos niveles de identación. callAFunction(document, element, window, "some string value", true, 123, navigator);

Ejemplo: Líneas en blanco.

if (wl && wl.length) { for (i = 0, l = wl.length; i < l; ++i) { p = wl[i]; type = Y.Lang.type(r[p]); if (s.hasOwnProperty(p)) { if (merge && type == 'object') { Y.mix(r[p], s[p]); } else if (ov || !(p in r)) { r[p] = s[p]; } } } }

if (wl && wl.length) { ! for (i = 0, l = wl.length; i < l; ++i) { ! p = wl[i]; type = Y.Lang.type(r[p]); ! if (s.hasOwnProperty(p)) { ! if (merge && type == 'object') { Y.mix(r[p], s[p]); } else if (ov || !(p in r)) { r[p] = s[p]; } } } }

Ejemplo: Nombramiento.

// Bien var limite = 30; var asuntoMensaje = "Mantenimiento"; var encontrado = true; var MAX_PERSONAS = 100; !// Mal: Fácil confusión con funciones var getLimite = 10; var isEncontrado = true; !// Bien function getName() { return myName; } !// Mal: Fácil confusión con variable function theName() { return myName; }

PRÁCTICAS DE PROGRAMACIÓN

Las prácticas de programación son otra convección de código, ayudándonos a resolver problemas cotidianos y estableciendo pautas para la reutilización y mantenibilidad del código.

A continuación veremos conceptos que para muchos posiblemente sean ya conocidos pero que son dignos de destacar.

.box {

width: expression(document.body.offsetWidth + "px");

}

<button onClick="doSomething()" id="boton">Haz clic aquí</button>

var element = document.getElementById("boton");

elememt.style.color = "red";

element.style.float = "left";

element.innerHTML = "<strong>CLICA AQUÍ</strong>;

var boton = document.getElementById("boton");

boton.addEventListener("click", doSomething, false);

#boton {

color: "red"; float: "left";

}

// tres alternativas vistas más adelante

ACOPLAMIENTO UI & LÓGICAUn factor determinante para la mantenibilidad de un proyecto es reducir el acoplamiento tanto como sea posible.

En la imagen podemos ver las capas de una aplicación web (client-side).

Siguiendo una serie de consejos podemos mantener estas capas lo más desacopladas posible.

REDUCCIÓN DE ACOPLAMIENTO

• Mantener JavaScript fuera de CSS.

• Mantener CSS fuera de JavaScript.

• Mantener JavaScript fuera de HTML.

• Mantener HTML fuera de JavaScript.

MANTENER HTML FUERA DE JS

Este es el punto más importante e interesante de esta sección, ya que el resto de puntos son relativamente lógicos.

En muchísimas ocasiones, ya sea por la falta de conocimiento o por comodidad, empezamos a desarrollar aplicaciones JS y empezamos a embeber código HTML en la lógica de la aplicación. Hay unas alternativas que podemos utilizar para evitarlo:

• Cargar HTML desde el servidor.

• Plantillas simples en el lado del cliente.

• Plantillas complejas en el lado del cliente.

function loadDialog(name, oncomplete) { ! var xhr = new XMLHttpRequest(); xhr.open("get", "/js/dialog/" + name, true); ! xhr.onreadystatechange = function() { ! if (xhr.readyState == 4 && xhr.status == 200) { var div = document.getElementById("dlg-holder"); div.innerHTML = xhr.responseText; ! oncomplete(); } else { // handle error } }; ! xhr.send(null); }

Alternativa 1

<li><a href="%s">%s</a></li> !function sprintf(text) { var i=1, args=arguments; ! return text.replace(/%s/g, function() { return (i < args.length) ? args[i++] : ""; }); } !// uso var result = sprintf(templateText, "/item/4", "Fourth item");

Alternativa 2

<script type="text/x-handlebars-template" id="list-item"> <li><a href="{{url}}">{{text}}</a></li> </script> !var script = document.getElementById("list-item"), templateText = script.text, template = Handlebars.compile(script.text); !var result = template({ text: "Fourth item", url: "/item/4" });

Alternativa 3

GLOBALIZACIÓNEl entorno de JavaScript tiene su ejecución asociada a un objeto global (en los navegadores web window).

Por lo tanto, cualquier variable definida fuera de una función se considera una variable global.

var numero = 6; !function mostrarNumero() { alert(numero); } !alert(window.numero); //=> 6 alert(typeof window.getNumero); //=>"function"

fichero1.js ...

ficheroN.js

• Entre los problemas de la globalización podemos nombrar los siguientes:

• Colisión de nombres.

• Fragilidad del código.

• Dificultad en el testeo.

• Globales accidentales.

AUTOMATIZACIÓN• Primeras páginas web sólo contenían algunos ficheros JS, los scripts eran

desarrollados por una persona.

• Hoy en día las aplicaciones web contienen gran cantidad de scripts, con miles de líneas de código programadas, en ocasiones, por más de una docena de programadores.

• Con este contexto, las metodologías utilizadas hace años dejan de servir.

• Incluir JS en los procesos de automatización es un paso muy importante para conseguir proyectos que puedan ser mantenidos.

VENTAJAS• El código de desarrollo no

tiene que estar "reflejado" en el entorno de producción.

• Pueden ser ejecutados análisis automáticos para detectar fallos o errores.

• Procesamiento de los ficheros JS.

• Fase de testeo es automatizada.

• Desplegado en los servidores de producción es sencillo.

• Tareas comúnes pueden ser ejecutadas numerosas veces de manera sencilla.

• Desarrolladores podrían necesitar ejecutar un "build" mientras hacen cambios en entornos de desarrollo. (Preferencia por refrescar en el navegador).

• El código de producción no es exactamente como el desarrollado. Seguimiento de bugs.

• Desarrolladores menos técnicos podrían tener problemas usando los sistemas de construcción.

DESVENTAJAS

AUTOMATIZACIÓN: ESTRUCTURA DE DIRECTORIO Y FICHERO

El primer paso es establecer una estructura de directorios y ficheros. La estructura dependerá directamente del tipo de proyecto que estemos desarrollando. Esta etapa es importante ya que el proceso de construcción dependerá directamente de la organización de ficheros y directorios, por lo que tener una estructura lógica es fundamental.

INDEPENDIENTE AL TIPO DE PROYECTO...

• Un objeto por archivo: Al igual que otros lenguajes de programación. Reduce el riesgo de trabajar varias personas en el mismo fichero.

• Agrupar archivos relacionados en directorios: Si existen objetos relacionados es aconsejable mantenerlos en un mismo directorio (p.e. Utilidades)

• Mantener librerías separadas del código: Cualquier código no desarrollado por nosotros debería mantenerse apartado del nuestro. De hecho, lo ideal es hacer uso de un CDN.

• Determinar la ruta de construcción: Debe ser un directorio completamente separado del directorio fuente, ya que este no debería ser tenido en cuenta en ningún chequeo u otro proceso automatizado.

• Mantener código de testeo cerca: Esto ayuda a los desarrolladores a echar en falta cualquier código de testeo que todavía no haya sido implementado.

• En cualquier caso: son recomendaciones, la estructura debe ser decidida por el equipo de desarrollo ya que en muchos casos dependeremos del framework utilizado en el lado del servidor.

LAYOUT BÁSICOUn layout básico suele ser el siguiente:

• build: Para los ficheros construidos finales.

• src: Para todos los ficheros desarrollados, conteniendo subdirectorios.

• test o tests: Para todos los tests de nuestra aplicación.

CSSLint

jQuery

YUI3

ANTLa elección de un sistema de construcción depende de las herramientas con las cuales los desarrolladores sean más familiares. Ya que las aplicaciones web suelen ir acompañadas de un back-end (posiblemente) desarrollado en Java, esta herramienta resulta muy apropiada para la tarea en cuestión. Aunque hay muchas alternativas a día de hoy, nosotros nos vamos a centrar en esta herramienta por su larga trayectoria y su sencillo uso.

Nos centraremos en la construcción de JavaScript, pero las técnicas vistas podrían ser aplicadas a otros lenguajes.

En primer lugar, lo básico:

• task: Es un paso del proceso (p.e. copiar archivos, ejecutar un comando, etc).

• target: Un grupo de tareas con un orden secuencial.

• project: Contenedor de todo lo anterior.

<project name="betabeers" default="hello"> <target name="hello"> <echo>Hello world!</echo> </target> <target name="goodbye" depends="hello"> <echo>Goodbye world!</echo> </target> </project>

Ejemplo de build.xml

En nuestro fichero de construcción podemos necesitar cierta información como, por ejemplo, el directorio src de nuestro código, el directorio que contiene las librerías o el directorio resultante del fichero JavaScript final.

Toda esta información puede definirse mediante propiedades y ser cargadas posteriormente desde nuestro fichero de construcción.

PROPIEDADES !

version = 0.1.0 src.dir = ./src lib.dir = ./lib build.dir = ./build

CARGAR PROPIEDADES !

<project name="betabeers" default="version"> <loadproperties srcfile="build.properties" /> <target name="version"> <echo>La version es ${version}</echo> </target> </project>

VALIDACIÓN• JavaScript no es un lenguaje compilado, por lo que

los desarrolladores no tienen la información que proporciona ese paso.

• Añadiremos JSHint (visto anteriormente) a nuestro proceso de construcción.

• Encontrar archivos a validar : <fileset> & <filelist>.

• Ejecutar JSHint a través de Rhino.

• Detener la construcción en caso de error.

<target name="validate"> <apply executable="java" failonerror="true" parallel="true"> <fileset dir="${src.dir}" includes="**/*.js" /> <arg line="-jar"/> <arg path="${rhino}"/> <arg path="${jshint}" /> <arg line="${jshint.options}" /> <srcfile/> </apply> </target>

src.dir = ./src lib.dir = ./lib rhino = ${lib.dir}/js.jar jshint = ${lib.dir}/jshint.js jshint.options = curly=true,forin=true,latedef=true,noempty=true,undef=true\ ,rhino=false

CONCATENACIÓN Y "BAKING"

• Si hemos seguido la recomendación de un objeto por archivo, tendremos docenas de ficheros.

• La concatenación reduce peticiones al servidor, reduciendo así el overhead producido por las cabeceras HTTP.

• Cada proyecto tendrá su propia directiva a la hora de concatenar los ficheros, veremos que con Ant es una tarea bastante sencilla de realizar.

CONCATENACIÓN: PASOS• Encontrar ficheros a concatenar, teniendo en

mente cuál debería ser su orden.

• Determinar el fin de línea.

• ¿Necesitamos cabeceras o pie de página?

<target name="concatenate"> <concat destfile="${build.dir}/build.js" fixlastline="yes" eol="lf"> <header>/* Build Time: ${build.time} */</header> <filelist dir="${src.dir}" files="first.js,second.js" /> <fileset dir="${src.dir}" includes="**/*.js" excludes="first.js,second.js"/> </concat> </target>

<tstamp> <format property="build.time" pattern="dd/MM/yyyy hh:mm:ss" locale="es,ES"/> </tstamp>

BAKING: DESCRIPCIÓN Y PASOS

• Esta etapa de la construcción puede ser util para añdir información de licencias o reemplazar cierto texto en nuestros ficheros JS.

• El ejemplo visto anteriormente es un tipo de baking.

• Lo veremos más claro con el siguiente ejemplo.

// Javascript var MyProject = { version: "@VERSION@" }; !// Ant <target name="bake"> <replaceregexp match="@VERSION@" replace="${version}" flags="g" byline="true"> <fileset dir="${build.dir}" includes="**/*"/> </replaceregexp> </target>

MINIMIZACIÓN & COMPRESIÓN

La minimización y compresión son los últimos pasos mas relevantes de la construcción. La minimización consiste en eliminar espacios en blanco, comentarios y realizar algunas mejoras en los ficheros para hacerlos tan pequeños como sea posible.

Por otro lado podemos comprimir los ficheros (gzip, etc).

276 KB

93,54 KB

MINIMIZACIÓNHay dos tipos: con parseo de código o mediante expresiones regulares. Los primeros son más seguros y suelen producir menor número de fallos de sintaxis.

• YUI Compressor: Julien Lecomte.

• Closure compiler : Google engineers.

• UglifyJS : Mihai Bazon.

COMPRESIÓNUna vez los ficheros han sido minimizados es hora de comprimirlos para transmitir el menor número de bytes como sea posible.

• Compresión en tiempo de ejecución.

• Compresión en tiempo de construcción.

UNIENDO PIEZASEl proceso de construcción debería tener, al menos, tres targets diferentes:

• Develop: Target ejecutado en la etapa de desarrollo.

• Integration: Target ejecutado por el CI.

• Production: Target ejecutado al realizar un release.

UNIENDO PIEZAS: PASOS1. Limpiar directorio build. (Elimiar). 2. Crear directorio build. 3. Develop:

1. Validación. 2. Concatenación.

4. Integration:

1. Minificación. 2. Testeo. 3. Documentación.

5. Release:

1. Baking.

<project name="JS" default="build.dev"> <loadproperties srcfile="JS.properties" /> !<target name="init">

<mkdir dir="${build.dir}"/> </target> !<target name="clean">

<delete dir="${build.dir}"/> </target> !<target name="build.dev" depends="clean,init,validate,concatenate"> </target> !<target name="build.int depends="build.dev,minify,test,document"> </target> !<target name="build.release" depends="build.int,bake"> </target>

</project>

CONCLUSIONESEl mantenimiento de un proyecto software es determinante, tanto para los propios desarrolladores, como para la entidad para la que trabajen, ahorrando tiempo, esfuerzo y dinero.

Hay numerosas alternativas y elegir las apropiadas es igual de determinante.

Siguiendo una serie de consejos sencillos, podemos aumentar nuestra productividad y ser más eficientes, mejorando así nuestra faceta profesional.

¿PREGUNTAS?

BIBLIOGRAFÍA• C. Zakas, Nicholas. (2012). Maintainable JavaScript. O'Reilly Media, Inc.

• Crockford, Douglas. (2001). JavaScript: The world's most misunderstood programming language. Recuperado de www.javascript.crockford.com/javascript.html

• jQuery Foundation. (2014). jQuery. Recuperado de http://jquery.com/

top related