Cargando



Uso avanzado de CoffeeScript

En este tutorial veremos los usos avanzados que podemos darle a CoffeeScript, así como técnicas que nos permitirán alcanzar un nivel alto de calidad y de optimización en el código de nuestras aplicaciones.


may 08 2015 05:02
Profesional
may 08 2015 08:37
En tutoriales pasados estuvimos trabajando con clases logrando así que nuestra aplicación fuese más modular, incluso estuvimos viendo a profundidad como realizar el trabajo asincrónico usando CoffeeScript como lenguaje, dándonos así un abanico mayor de opciones para trabajar con el mismo.

Ya habiendo dominado estos conceptos es hora de ir un paso más allá, y utilizar lo que sabemos para escribir código más limpio, más funcional y por supuesto más poderoso. Es hora de aprender a convertirnos en power users de CoffeeScript.

Usando el contexto de manera adecuada


Ahora que ya sabemos cómo utilizar las clases en nuestra aplicación, solo es cuestión de tiempo para que nos encontremos con problemas con el contexto. Cuando estamos con funciones simples es bastante sencillo ver que data esta función tiene en su ámbito, la misma sabe de variables globales, variables definidas dentro de la función y cualquier variable que haya sido definida en el ámbito local cuando la función fue creada.

Pero cuando los métodos están enlazados a objetos, esto se vuelve un poco más complicado. Para ilustrar esto veamos un ejemplo donde podremos ver este problema y luego veremos como CoffeeScript nos puede ayudar:
class Barco
			    levantarAncla: (doneCallback) ->
							   console.log "Levantando ancla."
			    fijarVel: (velocidad) ->
							   console.log "Ajustando velocidad a #{velocidad}"
			    zarpar: ->
							   @levantarAncla
							   @fijarVel 70
Supongamos entonces de acuerdo a nuestro código que queremos zarpar de inmediato, para ello hacemos lo siguiente para invocar a nuestra función:
bot = new Barco
bot.zarpar()
SI nos fijamos bien y trasladamos este código al mundo real, podemos darnos cuenta que levantar el ancla no sucede de manera inmediata, debemos esperar que el ancla este completamente levantado para poder zarpar. Este problema lo podemos solucionar añadiendo un callback y preguntando si el mismo se completó, así sabremos cuanto tiempo se demora esta acción y llamará a nuestra función una vez finalizada, veamos:
levantarAncla: (doneCallback) ->
			    console.log "Levantando ancla."
			    if doneCallback?
							   setTimeout doneCallback, 1000
Como podemos notar solo estamos invocando el callback si existe, de esta forma nos aseguramos que de que se complete este proceso, y por ende debemos modificar nuestra función zarpar:
zarpar: ->
							   @levantarAncla ->
											   @fijarVel 70
Ahora lo que hacemos es invocar la función fijarVel luego de levantar el ancla, esto nos asegura que no nos moveremos hasta que el ancla este completamente levantado. Esto luce bastante bien, vamos a compilar nuestro código y vamos a incluir el archivo .js generado en un HTML para ver la respuesta por consola:

uso-avanzado-coffeescript.jpg


Como vemos en la imagen hemos obtenido un error donde dice que la función no existe. ¿Qué ha pasado? Es muy sencillo, JavaScript ha fijado el valor this en la manera en la cual la función ha sido invocada, ya que al momento de llamar a bot.zarpar el valor this esta enlazado al objeto bot, por lo que this está enlazado al contexto global y esto no es lo que queremos.

Lo que queremos hacer es asegurarnos de que this siempre este enlazado a la instancia de bot dentro del cuerpo del callback y estamos de suerte, ya que CoffeeScript tiene una funcionalidad para ese caso. Para ello declararemos la función con fat arrow o flecha gruesa, de esta forma la función tendrá el this enlazado al contexto en el cual fue declarado, veamos como luce nuestro código con este cambio:
class Barco
			    levantarAncla: (doneCallback) ->
							   console.log "Levantando ancla."
							   if doneCallback?
											   setTimeout doneCallback, 1000
			    fijarVel: (velocidad) ->
							   console.log "Ajustando velocidad a #{velocidad}"
			    zarpar: ->
							   @levantarAncla =>
											   @fijarVel 70


bot = new Barco
bot.zarpar()
Vamos a compilar nuestro archivo y ver de qué forma CoffeeScript logro enlazar con la funcionalidad de flecha gruesa:

uso-avanzado-coffeescript-2.jpg


Lo que hace CoffeeScript antes de declarar el callback es fijar una variable local _this, la cual hace referencia a this, ya que a pesar que el callback está dinámicamente atado al valor todavía carga el contexto local en el cual fue declarado. Para finalizar vamos a ejecutar nuestro archivo generado y veamos entonces como el error fue solucionado:

uso-avanzado-coffeescript-3.jpg


Usando memoization


Ya habiendo visto la forma de solucionar el problema del contexto en nuestras aplicaciones con CoffeeScript vamos a ver una técnica bastante sencilla pero poderosa para ayudarnos a ahorrarnos trabajo. No es una técnica avanzada pero es una forma lógica de hacer una mejora en nuestro código sin mucho esfuerzo de nuestra parte.

Memoization
Lo que nos permite la técnica de memoization es de almacenar valores de una función en vez de recalcularlos cada vez que la función es llamada. Ahora que ya sabemos cómo usar clases y objetos podemos valernos de estos conocimientos para aplicarlos dentro de CoffeeScript y utilizar la técnica en cuestión.

Existen muchas formas de realizar el proceso de memoization, para el caso de este tutorial mantendremos las cosas simples. Para ello lo que haremos es que cuando cierta información es solicitada verificaremos si esta almacenada, si es así la retornamos de inmediato, en el caso contrario la podemos calcular y guardar para su uso en el futuro. Esta técnica es sumamente útil cuando necesitamos utilizar un algoritmo complejo para recibir una respuesta o en el caso que usemos una red lenta para obtener información.

Veamos entonces el código para ilustrar esta técnica:
class Cohete
			    obtenerTrayectoria: ->
							   @trayectoria ?= @hacerProcesoComplejoMatematico()
Para explicar mejor esta porción de código vamos a compilar el mismo para ver como CoffeeScript construye el JavaScript que tendrá nuestra técnica para ahorrarnos trabajo en nuestro desarrollo, veamos como luce nuestro código:


Como podemos ver en nuestro código, el cálculo de la trayectoria solo se llevará a cabo la primera vez que se haga el request y el valor almacenado se usará de ahora en adelante. Pudimos además ver en nuestro código CoffeeScript que tuvimos la ayuda del operador terciario ?= el cual evaluará la expresión en el caso que la trayectoria sea null, adicionalmente tendremos ayuda del retorno implícito de las funciones el cuál retornará el resultado de la expresión, en este caso el valor de @trayectoria si ha sido previamente almacenado o se acaba de calcular.

Pero esto no es todo lo que podemos hacer con nuestra técnica nueva en CoffeeScript, incluso podemos almacenar más de un valor usando una estructura de datos, veamos cómo podemos hacerlo:
class PuertaSeguridad
			    tieneAcceso: (guardia) ->
							   @acceso ?= {}
							   @acceso[guardia.numero_placa] ?= verificarCredenciales guardia.numero_placa
Esta porción de código lo que hace es que en nuestro objeto se almacena el resultado para cada guardia que ha pedido el acceso, solo nos faltaría algo único para poder identificarlos en nuestro objeto por lo que usamos el número de placa para esta tarea, veamos cómo se traduce nuestro código cuando lo compilamos:


Es importante mencionar que esta técnica solo debe ser utilizada con información que no cambiará en el transcurso de la ejecución de nuestro programa, en el caso que sea así recomendamos implementar una solución basada en cache.

Objetos de opciones


Por último veremos una manera de pasarle opciones a una función, esto no es una funcionalidad especial de CoffeeScript, es más una convención que hace uso de muchas de las características del lenguaje, utilizándolas en un patrón que es fácil de entender y que es igual de útil en muchas situaciones que se nos puedan presentar.

¿Cómo funciona?
La idea detrás de esto es simple, es tener una función que acepte este objeto de opciones el cual puede contener llaves asociativas para los argumentos de esa función. Esto hace que las opciones sean fáciles de entender desde el código en el cual se están llamando porque existen llaves para identificar que hace cada valor. Esto también disminuye los problemas de mantener a la vista los argumentos así como su orden, ya que las llaves de los objetos no son dependientes de esto y puede ser omitido si no es necesitado.

Para implementar los objetos de opciones primero vamos a utilizar argumentos opcionales para colocar por defecto en un argumento vacío. De esta forma al realizar el llamado podemos omitir las opciones en el caso que los valores no sean necesitados:
lanzarNave = (name, options={}) ->
			    return if options.marchaSeco
			    despegar()
Ahora usaremos el operador terciario ?= para llenar los valores de las opciones los cuales deseamos que tengan un valor especial por defecto:
lanzarNave = (name, options={}) ->
			    options.conteo ?= 10
			    console.log "#{i}..." for i in [options.conteo..0]
			    return if options.marchaSeco
			    despegar()
Definimos un último valor y nos ayudamos del operador ? en el caso de que este sea usado en un solo lugar:
lanzarNave = (name, options={}) ->
			    verificarCombustible(options.esperarComb ? 100)
			    options.conteo ?= 10
			    console.log "#{i}..." for i in [options.conteo..0]
			    return if options.marchaSeco
			    despegar()
Por último nos aprovechamos de la sintaxis permisiva de CoffeeScript para enviar las opciones a nuestra función sin los corchetes, dándonos una llamada bastante simple y natural:
lanzarNave "Millennium Falcon", marchaSeco: true, countdown: 15
Para finalizar vamos a compilar nuestro archivo y veamos la salida de nuestro código en JavaScript:


Con esto último finalizamos este tutorial, donde pudimos aprender no solo formas avanzadas de utilizar CoffeeScript sino técnicas que nos ayudarán a escribir mejor código, que con el uso y la investigación constante podremos convertirnos en mejores desarrolladores que usan las mejores prácticas para el desarrollo de aplicaciones.

¿Te ayudó este Tutorial?


Sin comentarios, sé el primero!

No esperes más y entra en Solvetic
Deja tus comentarios y aprovecha las ventajas de la cuenta de usuario ¡Únete!

X