coffeescript
Create successful ePaper yourself
Turn your PDF publications into a flip-book with our unique Google optimized e-Paper software.
CoffeeScript<br />
Un pequeño gran libro<br />
Javi Jimenez<br />
Este libro está a la venta en http://leanpub.com/<strong>coffeescript</strong><br />
Esta versión se publicó en 2013-12-17<br />
This is a Leanpub book. Leanpub empowers authors and<br />
publishers with the Lean Publishing process. Lean Publishing is<br />
the act of publishing an in-progress ebook using lightweight tools<br />
and many iterations to get reader feedback, pivot until you have<br />
the right book and build traction once you do.<br />
This work is licensed under a Creative Commons<br />
Attribution-NonCommercial-NoDerivs 3.0 Unported License
¡Twitea sobre el libro!<br />
Por favor ayuda a Javi Jimenez hablando sobre el libro en Twitter!<br />
El hashtag sugerido para este libro es #libro<strong>coffeescript</strong>.<br />
Descubre lo que otra gente está diciendo sobre el libro haciendo<br />
click en este enlace para buscar el hashtag en Twitter:<br />
https://twitter.com/search?q=#libro<strong>coffeescript</strong>
Dedicado a todas las personas que me sufren día a día.
Índice general<br />
Agradecimientos . . . . . . . . . . . . . . . . . . . . . . . . 1<br />
Prefacio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2<br />
JavaScript, El lenguaje padre . . . . . . . . . . . . . . . . 3<br />
CoffeeScript, El hijo bastardo . . . . . . . . . . . . . . . 4<br />
Un libro por el mundo . . . . . . . . . . . . . . . . . . . 5<br />
1. Comenzando . . . . . . . . . . . . . . . . . . . . . . . . 7<br />
1.1 Entorno necesario . . . . . . . . . . . . . . . . . . . . 7<br />
1.2 Instalando NodeJS . . . . . . . . . . . . . . . . . . . . 8<br />
1.3 Instalando CoffeeScript . . . . . . . . . . . . . . . . . 11<br />
2. Sintaxis . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13<br />
2.1 Valores, variables y comentarios . . . . . . . . . . . . 13<br />
2.2 Interpolación de cadenas . . . . . . . . . . . . . . . . 15<br />
2.3 Control de flujo . . . . . . . . . . . . . . . . . . . . . 16<br />
2.4 Loops . . . . . . . . . . . . . . . . . . . . . . . . . . 18<br />
2.5 Alias y Operadores . . . . . . . . . . . . . . . . . . . 22<br />
3. Funciones, Ámbito y Contexto . . . . . . . . . . . . . . 26<br />
3.1 Funciones . . . . . . . . . . . . . . . . . . . . . . . . 26<br />
3.2 Funciones y Argumentos . . . . . . . . . . . . . . . . 27<br />
3.3 Llamando a funciones . . . . . . . . . . . . . . . . . . 31<br />
3.4 Ámbito . . . . . . . . . . . . . . . . . . . . . . . . . . 33<br />
3.5 Contexto . . . . . . . . . . . . . . . . . . . . . . . . . 36<br />
3.6 Cambio de contexto en funciones . . . . . . . . . . . 39
ÍNDICE GENERAL<br />
4. Objetos y Arrays . . . . . . . . . . . . . . . . . . . . . . 41<br />
4.1 Recordando JavaScript y sus Objetos . . . . . . . . . . 41<br />
4.2 Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . 42<br />
4.3 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . 45<br />
4.4 Comprensiones . . . . . . . . . . . . . . . . . . . . . 50<br />
5. Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52<br />
5.1 Prototipos . . . . . . . . . . . . . . . . . . . . . . . . 52<br />
5.2 Clases . . . . . . . . . . . . . . . . . . . . . . . . . . 55<br />
5.3 Herencia . . . . . . . . . . . . . . . . . . . . . . . . . 58<br />
5.4 Polimorfismo . . . . . . . . . . . . . . . . . . . . . . 61<br />
6. Modularización . . . . . . . . . . . . . . . . . . . . . . . 63<br />
6.1 Namespacing . . . . . . . . . . . . . . . . . . . . . . 63<br />
6.2 Mixins . . . . . . . . . . . . . . . . . . . . . . . . . . 64<br />
6.3 Extendiendo clases . . . . . . . . . . . . . . . . . . . 65<br />
7. Bibliografía . . . . . . . . . . . . . . . . . . . . . . . . . 68
Agradecimientos<br />
Escribir un libro en estos tiempos tan sumamente acelerados supone<br />
un gran esfuerzo. Principalmente necesitas tiempo y normalmente<br />
la gente no está dispuesta a dártelo; es por eso que doy las gracias<br />
a todas esas personas que me han permitido gastar mi tiempo en lo<br />
que hoy considero importante.<br />
Dar las gracias a mi familia por siempre haberme permitido tener<br />
la libertad que he necesitado para poder equivocarme, conocerme<br />
y ser la persona que hoy soy.<br />
Dar las gracias al equipo de Tapquo¹: Ina, Cata, Oihane, Janire y<br />
Joseba por continuar con el día a día de nuestra loca empresa y<br />
seguir con el propósito que nos hemos marcado juntos.<br />
Dar las gracias a accionistas, consejeros y amigos de Tapquo por<br />
confiar en mi y darme la libertad para escaparme del país y<br />
centrarme en este texto que ahora lees.<br />
Dar las gracias también a Jeremy Ashkenas por crear el lenguaje<br />
CoffeeScript y contribuir altruistamente a la evolución del mundo<br />
web, sin olvidar a los numerosos contribuyentes al proyecto, y a<br />
otros proyectos OpenSource, que por suerte son demasiados para<br />
nombrarlos uno a uno aquí.<br />
Por último quiero darte las gracias a ti, gracias por descargarte<br />
este libro y darme una oportunidad de contarte lo que ha supuesto<br />
CoffeeScript en mi vida. Lo mucho que nos ha ayudado dentro de<br />
Tapquo creando un mejor código, más mantenible y más comprensible.<br />
Ahora solo te pido una última cosa, comparte este libro cómo lo<br />
he hecho yo contigo, regálalo y ayúdame a transmitir este contenido<br />
por el mundo.<br />
¹http://tapquo.com
Prefacio<br />
Tal vez no sabes muy bien porqué estás leyendo este pequeño libro<br />
sobre CoffeeScript, antes de nada piensa en este libro como un<br />
regalo que te ofrezco, no espero nada a cambio, únicamente que<br />
disfrutes con su lectura tanto como yo he disfrutado escribiéndolo.<br />
Te preguntarás por qué he decidido escribir un libro en castellano<br />
sobre CoffeeScript, y no sobre JavaScript como muchos esperaban<br />
que hiciera. Razones tengo muchas pero la principal es que tal<br />
vez sea el lenguaje con el que más me he divertido mientras lo<br />
descubría y el que más me ha ayudado a mejorar mis capacidades<br />
como desarrollador web. No te puedes hacer una idea lo que ha<br />
supuesto CoffeeScript para mi y por lo tanto para la empresa que<br />
fundé, Tapquo². Quiero contarte mis inicios con este maravilloso<br />
lenguaje y también su propia génesis, comencemos.<br />
La primera vez que escuché de CoffeeScript fue gracias a mi amigo<br />
Guillermo Pascual³ cuando en Septiembre del 2011 estábamos en la<br />
primera oficina de Tapquo desarrollando, y refactorizando, lo que<br />
iba a ser la primera versión de LungoJS⁴. Al principio no le presté<br />
mucha atención a CoffeeScript, pretendía ser mejor programador<br />
en JavaScript y Lungo iba a ser mi primer gran exponente, tenia<br />
puesto el Focus y no debía dispersarme con eso que nos gusta tanto<br />
a los Developers; aprender un nuevo lenguaje.<br />
Fue en Junio del 2012, una vez que Lungo ya era estable y yo ya<br />
dominaba JavaScript como pretendía, cuando dediqué mi tiempo<br />
a CoffeeScript. Comencé leyendo varios libros, tampoco había<br />
muchos más, los cuales me han ayudado a escribir el libro que<br />
²http://tapquo<br />
³http://twitter.com/pasku1<br />
⁴http://lungo.tapquo.com
Prefacio 3<br />
estas ahora leyendo. Enseguida obtuve el Flow con el lenguaje, ya<br />
que como proscrito Rubista y actual adorador de Python⁵, tanto la<br />
sintaxis como las estructuras me resultaban muy familiares. Tanto<br />
es así que en dos semanas de estudio me decidí a crear un micro<br />
Framework MVC, Monocle⁶, el cual intentaba aprovechar lo mejor<br />
de CoffeeScript y que actualmente además de usarlo activamente<br />
en nuestros locos días de oficina lo usan miles de desarrolladores<br />
por todo el mundo.<br />
Solo me queda pedirte una cosa, ¡diviértete!, diviértete aprendiendo<br />
este lenguaje lleno de buenas intenciones y de alguna que otra<br />
sorpresa.<br />
JavaScript, El lenguaje padre<br />
Si recibiese un BitCoin por cada vez que oigo que JavaScript no es<br />
un gran lenguaje ahora mismo tendría un buen puñado de monedas<br />
virtuales en mi bolsillo. JavaScript nunca ha sido, ni será, el lenguaje<br />
de programación más utilizado y más famoso del mundo, para eso<br />
tenemos a C o Java. Voy a ponerte en contexto, fue desarrollado<br />
en apenas 10 días por el genio Brendan Eich⁷ con la única premisa:<br />
tener una sintaxis muy parecida al lenguaje C, y esa es la única similitud<br />
con el lenguaje C. Como rezan algunos “cualquier parecido con<br />
la realidad es pura casualidad” y es por eso que desde sus inicios el<br />
nombre ha generado muchas equivocaciones, confundiendolo con<br />
una versión ligera de Java para la web.<br />
Lo que es seguro es que desde sus inicios en 1995 JavaScript ha<br />
seguido en constante evolución:<br />
• 1995 Primera versión<br />
• 1998 ECMA-262 1st + 2nd edition<br />
⁵https://en.wikipedia.org/wiki/Python_(programming_language)<br />
⁶http://monocle.tapquo.com<br />
⁷https://en.wikipedia.org/wiki/Brendan_Eich
Prefacio 4<br />
• 2000 ECMA-262 3rd edition<br />
• …<br />
• 2005 AJAX, Array Extras, string generics… (v1.5)<br />
• 2006 Iterators, Pythonic generators… (v1.6)<br />
• 2008 Generator expressions, expression closures, JSON support…(v1.7-<br />
v1.8)<br />
• …<br />
• 2010 EcmaScript5 (v1.8.2)<br />
• …<br />
• 2014 EcmaScript6<br />
El gran problema que ha tenido JavaScript es que la forma de<br />
desarrollar nunca ha sido comprendida ni para los desarrolladores<br />
de la vieja escuela (C, Java…), que se quejaban de la falta de clases y<br />
demás singularidades de la OOP, ni para los que solo han trabajado<br />
con lenguajes modernos (Python, Ruby, Haskell…), que maldecían<br />
la verbosidad barroca, con las conocidas llaves, paréntesis y puntos<br />
y comas. Por lo tanto siempre ha sido criticado y en mi opinión<br />
siempre lo será. Como todos los lenguajes, JavaScript tiene defectos,<br />
pero poco a poco y con cada nueva revisión se van puliendo y<br />
mejorando.<br />
En este libro vamos a acercarnos a JavaScript por medio de CoffeeScript,<br />
pretendo que seas mejor desarrollador JavaScript de lo<br />
que has sido hasta ahora. Mi propósito es que respetes al Lenguaje<br />
Padre y que puedas enfrentarte a proyectos con las suficientes lineas<br />
de código que asuste al mejor de los Gurus de jQuery⁸ (sarcasmo).<br />
CoffeeScript, El hijo bastardo<br />
CoffeeScript es un lenguaje de programación el cual es capaz de<br />
compilar su resultado a JavaScript, es unicamente eso. Está totalmente<br />
inspirado en la sintaxis de lenguajes como Ruby, Python y<br />
⁸http://jquery.com
Prefacio 5<br />
Haskell para capacitar a JavaScript de una expresividad y brevedad<br />
que por si solo no tiene. Tus programas en JavaScript se escribirán<br />
con menos lineas de CoffeeScript, mas o menos una proporción<br />
1/3, sin tener ninguna perdida de rendimiento (incluso a veces la<br />
mejora).<br />
Su autor es Jeremy Ashkenas⁹ y comenzó con el proyecto el 13<br />
de Diciembre del 2009 con un misterioso e intrigante comentario<br />
en su primer commit en GitHub "initial commit of the<br />
mystery language". Comenzó creando el compilador en Ruby<br />
pero en apenas 3 meses cambio de idea e hizo que el compilador<br />
estuviese escrito con su propio lenguaje, CoffeeScript. El proyecto<br />
pronto fue seguido por multitud de desarrolladores en GitHub¹⁰,<br />
donde tanto Jeremy como el resto de contribuyentes añadían nuevas<br />
características cada mes.<br />
Después de publicarse la versión 1.0 en navidades del 2010, CoffeeScript<br />
se convirtió en uno de los proyectos más seguidos en GitHub.<br />
El lenguaje volvió a tener un empujón en la escena web, cuando en<br />
Abril del 2011 David Heinemeier Hansson¹¹ confirmó los rumores<br />
que decían que CoffeeScript iba a estar incluido en la versión 3.1 de<br />
Ruby on Rails.<br />
Y yo me pregunto ¿como puede ser que un lenguaje tan pequeño<br />
haya captado tanto interés? Tres razones me vienen a la mente:<br />
divertido, seguro y fácil de leer.<br />
Un libro por el mundo<br />
Este libro fue escrito durante mi viaje por Tailandia, en los meses<br />
de Noviembre y Diciembre del año 2013. Decidí irme a 12.000km<br />
de mi lugar de residencia para centrarme en el libro y poder dar<br />
lo mejor de mi en él. Uno de mis propósitos en la vida es ofrecer<br />
⁹https://en.wikipedia.org/wiki/Jeremy_Ashkenas<br />
¹⁰https://github.com/jashkenas/coffee-script<br />
¹¹https://en.wikipedia.org/wiki/David_Heinemeier_Hansson
Prefacio 6<br />
mi conocimiento a todo aquel que quiera adquirirlo y esta es una<br />
buena manera de hacerlo. Ahora tú tienes que ayudarme a regalar<br />
este libro a tus amigos, no te ha costado nada y no te costará nada<br />
hacerlo.<br />
Mi próximo libro tratará sobre EcmaScript 6¹² y lo escribiré en<br />
Nueva Zelanda, por lo que si después de leer este libro te ha gustado<br />
tanto como para hacer una donación¹³, financiando así parte del<br />
próximo libro, te estaré eternamente agradecido.<br />
¹²https://en.wikipedia.org/wiki/ECMAScript<br />
¹³leanpub.com/<strong>coffeescript</strong>
1. Comenzando<br />
Si has leído el prefacio, se puede decir que ya conoces el origen de<br />
CoffeeScript, que es lo que trae y porque es una de las mejores cosas<br />
que le han pasado a los programadores web en estos últimos años.<br />
Ahora mismo no eres capaz de escribir una sola linea, así que te voy<br />
a ayudar a preparar tu entorno de trabajo con CoffeeScript.<br />
1.1 Entorno necesario<br />
En el prefacio has aprendido que el compilador de CoffeeScript esta<br />
escrito en CoffeeScript. Curiosidades aparte, tienes que ser capaz de<br />
resolver la siguiente cuestión:<br />
¿Cómo puedo ejecutar el compilador en mi sistema si todaviá<br />
no tengo el compilador de CoffeeScript?<br />
¿No? Te voy a ayudar a encontrar la solución preguntándote de<br />
nuevo; si hubiera una manera de ejecutar JavaScript en tu maquina<br />
sin utilizar un navegador web y mostrar el código al sistema<br />
operativo… ¿Sigues sin caer? ¿De verdad?<br />
Es muy sencillo, no hace falta más que utilizar NodeJS¹⁴ y he<br />
aquí donde viene un punto de aclaración; mucha gente entiende<br />
“Node” como un servidor web en JavaScript pero, por suerte, es<br />
mucho más que eso y cada vez está más presente en soluciones<br />
que nada tienen que ver con el mundo de los servidores web.<br />
Fundamentalmente NodeJS es un puerta entre tu código JavaScript<br />
y el sistema operativo, trayendo consigo una potente herramienta<br />
llamada NPM¹⁵, Node Package Manager, que lo hace extensible<br />
¹⁴http://nodejs.org<br />
¹⁵http://npmjs.org
1. Comenzando 8<br />
infinitamente. Si vienes del mundo de Ruby piensa en NPM como<br />
una analogiá a las Gems.<br />
El resto de este área se centrará en como instalar NodeJS, que por<br />
supuesto necesitaremos para usar CoffeeScript y su compilador.<br />
Pero si no puedes esperar más y quieres intentar codificar tus<br />
primeras lineas en CoffeScript echa un pequenõ vistazo al Try<br />
CoffeeScript¹⁶ de la página oficial.<br />
1.2 Instalando NodeJS<br />
Existen dos formas de instalar nodeJS, mediante el código fuente<br />
del proyecto (y su posterior compilación) o bien descargando una<br />
instalación con el proyecto ya compilado. Independientemente del<br />
sistema operativo que uses no tendrás grandes problemas, pero si<br />
que puedo decir que por mi experiencia los que más problemas<br />
suelen tener con la instalación han sido los usuarios de Linux.<br />
Veamos como instalarlo en cada uno de los sistemas operativos:<br />
Mac<br />
Si eres usuario como yo del maravilloso Brew¹⁷ (homebrew package<br />
manager), unicamente tendrás que ejecutar el comando:<br />
brew install node<br />
En caso contrario sigue los siguientes pasos:<br />
• Instala Xcode<br />
• Instala GIT<br />
• Ejecuta los siguientes comandos:<br />
¹⁶http://<strong>coffeescript</strong>.org<br />
¹⁷http://brew.sh/
1. Comenzando 9<br />
darwin_setup.sh<br />
git clone git://github.com/ry/node.git<br />
cd node<br />
./configure<br />
make<br />
sudo make install<br />
Linux (Ubuntu)<br />
Antes de compilar NodeJS necesitamos añadir las siguientes dependencias<br />
a tu sistema:<br />
sudo apt-get install g++ curl libssl-dev apache2-utils<br />
sudo apt-get install git-core<br />
Ahora ejecuta los mismos comandos que en la instalación en Mac:<br />
ubuntu_setup.sh<br />
git clone git://github.com/ry/node.git<br />
cd node<br />
./configure<br />
make<br />
sudo make install<br />
Windows<br />
Justo para este sistema voy a recomendar encarecidamente el uso de<br />
los downloads oficiales desde la sección de descargas¹⁸ de NodeJS.<br />
El cual lo único que te pedirá es reiniciar el sistema para que NodeJS<br />
esté disponible.<br />
¹⁸http://nodejs.org/download/
1. Comenzando 10<br />
Tu primer servidor NodeJS<br />
Vamos a crear un pequeño programa para comprobar que todo<br />
está funcionando correctamente, antes de ello ejecuta el siguiente<br />
comando para comprobar la versión de la que dispones:<br />
node -v<br />
Ahora viendo que tienes una versión reciente (por tu bien) vas a<br />
crear un pequeño servidor con un único fichero, a este le llamaras<br />
hello_node.js:<br />
hello_node.js<br />
var http = require('http');<br />
http.createServer(function (request, response) {<br />
response.writeHead(200, {'Content-Type': 'text/plain'\<br />
});<br />
response.end('Hello Node.js\n');<br />
}).listen(1337, "127.0.0.1");<br />
console.log('Server running at http://127.0.0.1:1337/');<br />
Ahora toca ver si el servidor arranca correctamente ejecutando el<br />
NCL (Node Command Line):<br />
node hello_node.js<br />
Si todo va bien deberías ver por tu pantalla el mensaje Server<br />
running at http://127.0.0.1:1337/, si es asi vete a tu browser<br />
a esa misma dirección y aparecerá por pantalla un bonito mensaje.<br />
Desde este momento puedo decirte que tienes NodeJS listo y funcionando<br />
para empezar con CoffeeScript.
1. Comenzando 11<br />
1.3 Instalando CoffeeScript<br />
Ahora que ya tenemos NodeJS la instalación de nuestro nuevo<br />
lenguaje favorito va a ser sencilla ya que utilizaremos NPM con<br />
el comando:<br />
sudo npm install -g coffee-script<br />
Como ves he puesto sudo (solo para mac/linux) y los parámetros<br />
install (evidente) y -g para que lo instale globalmente seguido de<br />
coffee-script que en este caso es el paquete que queremos instalar.<br />
Tras la instalación debes comprobar que versión de CoffeeScript<br />
tienes en el sistema:<br />
coffee -v<br />
Si aparece una nueva versión de CoffeeScript la actualización<br />
es relativamente sencilla ya que únicamente tienes que volver a<br />
utilizar NPM cambiando un único parámetro ¿ya sabes cual es?:<br />
sudo npm update -g coffee-script<br />
Ahora que ya tienes CoffeeScript vamos a ver si funciona todo<br />
correctamente creando una simple función desde el terminal con<br />
la consola coffee:<br />
coffee
1. Comenzando 12<br />
coffee> sum = (a, b) -> a + b<br />
coffee> sum 1, 2<br />
3<br />
coffee> sum 3, 4<br />
7<br />
Se que no sabes lo que has escrito, no es la idea de este capítulo,<br />
aunque por la sintaxis y los operadores puedes llegar a sacar una<br />
conclusión. En el siguiente capitulo vas a conocer más de la sintaxis<br />
de este lenguaje y descubrirás lo bien hecho que está y lo divertido<br />
que es programar con el.
2. Sintaxis<br />
Antes de comenzar con este capítulo asumo que tienes una base en<br />
JavaScript, y eso no quiere decir que sabes usar jQuery o cualquier<br />
otra librería. Sino es así te recomiendo que estudies el Lenguaje<br />
Padre antes de comenzar con el hijo bastardo. De una manera u otra<br />
mi responsabilidad como desarrollador JavaScript es recomendarte<br />
que al menos conozcas, leas y comprendas estos dos libros:<br />
• JavaScript, the good parts¹⁹ (Douglas Crockford)<br />
• Eloquent JavaScript²⁰ (Marijn Haverbeke)<br />
En ellos descubrirás algunas particularidades del lenguaje JavaScript<br />
y siempre te vendrá bien tenerlos a mano para poder repasar<br />
algún que otro concepto. En este capítulo vamos a aprender la<br />
sintaxis básica de CoffeeScript comprobando que comparte más<br />
similitudes con Ruby o Python que con JavaScript.<br />
2.1 Valores, variables y comentarios<br />
La creación y asignación de variables no difiere mucho de otro lenguaje<br />
de programación, tal vez la mayor diferencia con JavaScript<br />
es que no hace falta comenzar con var ni finalizar la declaración<br />
con el prehistórico caracter ;. Veamos un ejemplo sencillo:<br />
¹⁹http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742<br />
²⁰http://eloquentjavascript.net/
2. Sintaxis 14<br />
heroe = "Superman"<br />
year = 1984<br />
Sencillo ¿no?, en el siguiente capítulo conocerás como CoffeeScript<br />
gestiona automáticamente el ámbito de nuestras variables, escapando<br />
del temido e ineficiente GlobalScope.<br />
En el caso de que necesites introducir cadenas de texto de mayor<br />
tamaño podríamos hacerlo de la siguiente manera:<br />
crockford[1] = "JavaScript doesn't suck! You're just do\<br />
ing it wrong."<br />
crockford[2] = "JavaScript doesn't suck!<br />
You're just doing it wrong."<br />
Aunque parezca increíble los 2 elementos del array crockford<br />
tienen el mismo contenido, y a pesar de que en la segunda parece<br />
que hemos hecho un salto de linea, CoffeeScript y su parseador<br />
darán en ambos casos como resultado:<br />
> 'JavaScript doesn't suck! You're just doing it wrong.'<br />
En el caso de que quieras generar un salto de linea te recomiendo<br />
que utilices el delimitador de string """ en vez de utilizar las<br />
prehistóricas concatenaciones de \n y \r:<br />
crockford[3] = """<br />
JavaScript doesn't suck!<br />
You're just doing it wrong."""<br />
El resultado de crockford[3] será:
2. Sintaxis 15<br />
> 'JavaScript doesn't suck!<br />
You're just doing it wrong.'<br />
Los comentarios dentro de nuestro código siguen el mismo patrón,<br />
utilizando el carácter # como delimitador:<br />
# Tu primer comentario<br />
Los comentarios multilinea también están soportados, y como te<br />
puedes imaginar vas a tener que triplicar el caracter # para que sean<br />
efectivos:<br />
###<br />
Un comentario multilinea,<br />
por ejemplo definir la licencia<br />
o autor del código fuente.<br />
###<br />
2.2 Interpolación de cadenas<br />
CoffeeScript nos da la interpolación de string de una manera<br />
similar a como lo hace Ruby. Dentro de una cadena delimitada por<br />
el caracter " podremos introducir la expresión #{}, la cual puede<br />
contener cualquier tipo de expresión que será interpolada a string:<br />
hero = "Batman..."<br />
chat = """<br />
friend : What... is your favorite hero?<br />
soyjavi: #{hero}<br />
friend : Seriously?"""<br />
Vamos a ver ahora un ejemplo algo más completo con operaciones<br />
numéricas donde descubrirás algo más de mi:
2. Sintaxis 16<br />
born = new Date(1980, 4, 10)<br />
now<br />
= new Date().getFullYear()<br />
answer = "@soyjavi was born in #{born.getFullYear()},<br />
."<br />
now has #{now - born.getFullYear()} years old\<br />
En mi opinión la interpolación es una solución mucho más elegante<br />
que la concatenación a base de ir anidando strings, variables y el<br />
caracter +.<br />
2.3 Control de flujo<br />
En este apartado vas a ver las estructuras para el control del<br />
flujo de tu aplicación, recordando que CoffeeScript esta lleno de<br />
convenciones que facilitan la escritura de buen código. Por ejemplo,<br />
no es necesario utilizar paréntesis en las expresiones if, else, for,<br />
… ni delimitar el ámbito del flujo con las aburridas Curly Braces {}.<br />
Veamos un ejemplo:<br />
if year == 1980<br />
born = true<br />
else<br />
born = false<br />
En este caso vemos que el ámbito del if lo marca el nivel de<br />
indentación donde solo hay una linea born = true, aunque<br />
podrías introducir todas las que necesites, además vemos que no<br />
es necesario introducir () y {}. Se recomienda que cada nivel de<br />
indentación sea de 2 espacios, aunque hay desarrolladores que<br />
utilizan 4 espacios no es muy recomendable ya que con cada nivel<br />
perdemos espacio para escribir nuestro código. De todas formas,<br />
podemos hacerlo de una forma más elegante, expresiva y en una<br />
sola linea de la siguiente manera:
2. Sintaxis 17<br />
if year is 1980 then born = true else born = false<br />
Como ves en este caso he introducido el alias operador is que<br />
es traducido a JavaScript como ===. En ambos casos el código<br />
resultante en JavaScript será el mismo, pudiendo comprobar que<br />
esta lleno de todos esos caracteres y convenciones de las que<br />
queremos escapar:<br />
var born;<br />
if (year === 1980) {<br />
born = true;<br />
} else {<br />
born = false;<br />
}<br />
Otra manera de hacer la misma operación seria utilizando un<br />
operador ternario:<br />
born = (if year is 1980 then true else false)<br />
Traduciendose a JavaScript de una forma bastante similar:<br />
born = (year === 1980 ? true : false);<br />
Ahora sigamos con alguna expresión más, por ejemplo en el caso<br />
de que solo necesites hacer una comparación if podríamos hacerlo<br />
al estilo Ruby:<br />
born = true if year is 1980<br />
Si quisiésemos hacer la negación de la sentencia anterior, esto<br />
es; establecer la variable born a false, podríamos utilizar el alias<br />
operador isnt que se traduce en JavaScript a !==. Veamos como:
2. Sintaxis 18<br />
born = false if year isnt 1980<br />
o también utilizando la expresión unless:<br />
born = false unless year is 1980<br />
En ambos casos puedes ver que el JavaScript resultante es el mismo:<br />
var born;<br />
if (year !== 1980) {<br />
born = false;<br />
}<br />
Ya hemos visto los alias is, unless y isnt más adelante podrás<br />
conocer más alias disponibles en CoffeeScript los cuales aportarán<br />
una mayor expresividad a tu código.<br />
2.4 Loops<br />
Una de las estructuras que más solemos utilizar como desarrolladores<br />
son las iteraciones o bucles, esto se expone claramente en los<br />
proyectos JavaScript donde al no existir muchos métodos dedicados<br />
para el tratamiento de arrays debemos leer el contenido de los<br />
mismos de forma iterada.<br />
En CoffeeScript vas a empezar por lo básico, pero en capítulos<br />
posteriores podrás ver que disponemos de hacks dedicados a los<br />
arrays los cuales te solventarán la vida. Vamos a ver como podrías<br />
crear un simple bucle contador con la expresión while:
2. Sintaxis 19<br />
heroes = 0<br />
while heroes
2. Sintaxis 20<br />
heroes = ["Batman", "Spiderman", "Superman"]<br />
for hero in heroes by 2<br />
console.log hero<br />
Con el incremental by 2, el resultado será:<br />
> "Batman", "Superman"<br />
Al igual que con las estructuras if, while, until podemos resolver<br />
el anterior ejemplo en una única y expresiva linea:<br />
console.log hero for hero in ["Batman", "Spiderman", "S\<br />
uperman"] by 2<br />
Ahora voy a complicarlo un poco más, filtrando el array por un<br />
determinado condicionante; en este caso nos vamos a quedar con<br />
los heroes que tengan como inicial la letra S:<br />
heroes = ["Batman", "Spiderman", "Superman"]<br />
console.log hero for hero in heroes when hero[0] is "S"<br />
Quiero que leas el código resultante en JavaScript, para comprobar<br />
que las estructuras que genera CoffeeScript son totalmente correctas,<br />
respetando declaración y ámbito de variables:
2. Sintaxis 21<br />
var hero, heroes, _i, _len;<br />
heroes = ["Batman", "Spiderman", "Superman"];<br />
for (_i = 0, _len = heroes.length; _i < _len; _i++) {<br />
hero = heroes[_i];<br />
if (hero[0] === "S") {<br />
console.log(hero);<br />
}<br />
}<br />
En el caso de que necesites de estructuras iterativas abiertas, lo<br />
mejor es que utilices la expresión loop. La cual nos da un mayor<br />
control sobre el flujo de control de nuestro bucle ya que siempre<br />
está iterando hasta que se cumpla algún condicionante dentro de el<br />
y demos por finalizo el proceso con la expresión break. Veamos un<br />
ejemplo:<br />
heroes = 2<br />
loop<br />
break if heroes is 8<br />
heroes++<br />
console.log heroes<br />
Como puedes leer el bucle terminará cuando la variable heroes sea<br />
igual a 8, ya que la expresión break ejecuta la linea inmediata a<br />
la estructura loop. Vamos a echar un vistazo a como quedaría el<br />
código en JavaScript:
2. Sintaxis 22<br />
var heroes = 2;<br />
while (true) {<br />
if (heroes === 8 {<br />
break;<br />
}<br />
heroes++;<br />
}<br />
console.log(heroes);<br />
2.5 Alias y Operadores<br />
Ya sabes que una de las premisas de CoffeeScript es intentar crear<br />
código expresivo, por ahora hemos visto algunos operadores como<br />
is, isnt, unless y desde esta sección conoceremos más facilitadores<br />
para nuestro código CoffeeScript.<br />
Como ya sabes is es traducido al comparador === y su contrario<br />
isnt es traducido a !==, en el caso de que quieras hacer una<br />
negación ! deberás utilizar el operador not. Veamos un ejemplo:<br />
facebook_account = false<br />
if not facebook_account<br />
console.log "Well done!!"<br />
Ahora quiero hacerte una pregunta ¿Como lees en lenguaje natural<br />
los operadores en JavaScript || y &&?, me imagino que la respuesta<br />
que te habrás dado a ti mismo ha sido or y and. Bien, pues<br />
CoffeeScript intenta crear lenguaje natural en tu código facilitando<br />
que cualquier persona pueda entender lo que pretendes hacer con<br />
el. Veamos un ejemplo:
2. Sintaxis 23<br />
facebook_account = false<br />
twitter_account = false<br />
if not facebook_account and not twitter_account<br />
console.log "You're a caveman."<br />
El operador or también puedes utilizarlo para inicializar variables<br />
con valores por defecto. Pongamos el caso de que queremos almacenar<br />
en la variable where el valor de location solo cuando este<br />
sea distinto de undefined, en caso contrario establecemos el valor<br />
string "Gotham"<br />
where = location or "Gotham"<br />
El operador existencial ? puede ayudarnos a que el ejercicio anterior<br />
sea más preciso, dado que si la variable location no esta declarada<br />
dará un error de compilación. Utilizando ? nos aseguramos que<br />
comprueba que location no sea ni undefined ni null:<br />
where = location ? "Gotham"<br />
Veamos como queda esta mínima sentencia en JavaScript:<br />
var where;<br />
where = typeof location !== "undefined" && location !==\<br />
null ? location : "Gotham";<br />
El operador existencial ? te puede servir para muchos más contextos,<br />
por ejemplo imagina que tienes una librería basada en<br />
módulos y quieres llamar a un método solo si existe un determinado<br />
namespace:
2. Sintaxis 24<br />
batman.vehicles?.batMobile()<br />
Aquí vemos algo de la magia de CoffeeScript, en el caso de que la<br />
propiedad vehicles de batman no exista, no ejecutará el método<br />
batMobile. Traduciendo la anterior sentencia a JavaScript quedaría<br />
de la siguiente manera:<br />
var _ref;<br />
if ((_ref = batman.vehicles) != null) {<br />
_ref.batMobile();<br />
}<br />
Imagina ahora que quieres ejecutar el método kamehame siempre<br />
y cuando sea una función ejecutable, simplemente cambiando la<br />
posición del operador ? lo tendrás resuelto:<br />
goku.movements?.kamehame?()<br />
Echa un vistazo a su traducción en JavaScript:<br />
var _ref;<br />
if ((_ref = goku.movements) != null) {<br />
if (typeof _ref.kamehame === "function") {<br />
_ref.kamehame();<br />
}<br />
}<br />
Uno de los alias que más me sorprendió cuando aprendí CoffeeScript<br />
fue @ el cual se utiliza para sustituir el típico this:<br />
@twitter = "@soyjavi"
2. Sintaxis 25<br />
Lo que estamos haciendo en este caso es establecer la variable del<br />
contexto actual twitter con el valor "@soyjavi". En el próximo<br />
artículo veremos como podemos conmutar de contextos de una manera<br />
sencilla, lo cual nos será de gran ayuda en muchas ocasiones.<br />
El último alias que vas a ver será :: que hace referencia a prototype,<br />
entendiendo que con tu base actual de JavaScript sabes perfectamente<br />
a que me refiero.<br />
Goku::life = 10<br />
Si no sabes muy bien de lo que te estoy hablando has incumplido el<br />
pacto conmigo, ¡debes estudiar JavaScript!. En el capítulo 5, clases,<br />
darémos un pequeño repaso al paradigma de los prototipos en<br />
JavaScript con CoffeeScript.
3. Funciones, Ámbito y<br />
Contexto<br />
CoffeeScript elimina todo adorno barroco en la declaración de<br />
funciones tal y como estás acostumbrado en la programación con<br />
JavaScript. Lo sustituye por un símbolo simple y conciso como es -><br />
y como verás más adelante deberás dominarlo si quieres convertirte<br />
en un verdadero CoffeeScripter.<br />
Las funciones pueden declararse en una sola linea o en varias<br />
lineas indentadas como por ejemplo se hace en otros lenguajes<br />
como son Ruby o Python. Otra de las cosas maravillosas que te<br />
vas a encontrar es que no hace falta definir el return de nuestra<br />
función ya que existe una convención donde la última expresión que<br />
definamos dentro de una función automáticamente se convertirá en<br />
el resultado de la misma. En otras palabras no es necesario utilizar<br />
la sentencia return a menos que necesites que el flujo de tu función<br />
necesite devolver algo antes.<br />
3.1 Funciones<br />
Como vale más un ejemplo que mil palabras, con la siguiente linea<br />
te descubro tu primera función en CoffeeScript:<br />
hello = -> “world”<br />
Como puedes ver he creado una función llamada hello la cual<br />
devuelve automáticamente la cadena de texto “world”, lo se, no es<br />
una función pretenciosa pero si la compilas veras que el resultado<br />
en JavaScript cambia bastante:
3. Funciones, Ámbito y Contexto 27<br />
var hello = function() {<br />
};<br />
return “world”;<br />
Como he comentado anteriormente a menos que sea estrictamente<br />
necesario no es necesario declarar funciones en múltiples lineas.<br />
Pero la función hello podría codificarse de la siguiente manera:<br />
hello = -><br />
# ... amazing CoffeeScript code!<br />
“world”<br />
La compilación en JavaScript seguirá siendo exactamente igual que<br />
en la primera función hello, por lo que en este punto tu decidirás<br />
cuando y porqué utilizar funciones en una sola linea, recordando<br />
siempre el respeto por el Clean Code²².<br />
3.2 Funciones y Argumentos<br />
Como hemos visto la declaración de funciones es sumamente<br />
sencilla, pero el ejemplo anterior es poco versátil ya que únicamente<br />
devuelve un valor constante. Veamos una función algo más dinámica:<br />
divide = (a, b) -> a / b<br />
En esta ocasión tenemos una función divide que recibe dos argumentos<br />
a y b los cuales se dividen entre si y se devuelve el resultado.<br />
Esto mismo compilado en JavaScript:<br />
²²http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/<br />
0132350882
3. Funciones, Ámbito y Contexto 28<br />
var divide = function(a, b) {<br />
};<br />
return a / b;<br />
Una de las cosas que más cuesta en JavaScript es el control de<br />
valores en argumentos de una función, pero gracias a CoffeeScript el<br />
trabajo se simplifica considerablemente. Por ejemplo, imagina que<br />
en nuestra función divide queremos que el argumento b por defecto<br />
sea 1 si no introducimos ningún valor:<br />
divide = (a, b = 1) -> a / b<br />
Como ves sigue siendo muy sencillo y curiosamente sigue siendo<br />
una sola linea de código, pero si analizamos la compilación en<br />
JavaScript:<br />
var divide;<br />
divide = function(a, b) {<br />
if (b == null) {<br />
b = 1;<br />
}<br />
return a / b;<br />
};<br />
Si estudiamos el código vemos que controla si el argumento b es<br />
null, en caso de que lo sea automáticamente lo inicializa con el<br />
valor 1. Si no te acabas de convencer de porqué CoffeeScript es una<br />
de los mejores complementos para desarrollar Aplicaciones Webs,<br />
te recomiendo que cierres este libro y te dediques a otra cosa.<br />
Si estas leyendo esto es porque no te has negado a continuar<br />
aprendiendo CoffeeScript, ahora vas a ver un nuevo ejemplo que<br />
te hará subir de nivel:
3. Funciones, Ámbito y Contexto 29<br />
divide = (numbers...) -><br />
result = 0<br />
numbers.forEach (number) -> result = result / number<br />
result<br />
Como ves en este ejemplo he introducido el símbolo ... (splats)<br />
detrás del argumento numbers, haciendo que CoffeeScript espere un<br />
numero indeterminado de argumentos y que los vaya dividiendo<br />
y almacenando en la variable result. Veamos esto mismo en<br />
JavaScript:<br />
var divide,<br />
__slice = [].slice;<br />
divide = function() {<br />
var numbers, result;<br />
numbers = 1
3. Funciones, Ámbito y Contexto 30<br />
race = (winner, runners...) -> console.log winner, runn\<br />
ers.join(“,“)<br />
Utilizar un splat en runners hace que CoffeeScript espere un<br />
rango de valores y automáticamente los convierta en un array de<br />
elementos, siempre y cuando existan valores. Como vemos en el<br />
ejemplo continuamos con la declaración de funciones en una única<br />
linea pero si leemos el código en JavaScript el numero de lineas crece<br />
considerablemente:<br />
var race,<br />
__slice = [].slice;<br />
race = function() {<br />
var runners, winner;<br />
winner = arguments[0], runners = 2 # eat it?<br />
Los argumentos que no son de tipo splat se asignarán en primer<br />
lugar, por lo que si se llama a sandwich con dos únicos argumentos,<br />
estos pasarán a top y base. Sólo cuando hay tres o más argumentos<br />
se utilizará el argumento splat ingredients y con esto CoffeeScript<br />
vuelve a reafirmarse con la convención “simplicidad frente a complejidad”.<br />
Recordarte, como buen conocedor de los internals de JavaScript,<br />
que también podríamos no declarar ningún argumento y acceder a<br />
ellos gracias al array arguments:
3. Funciones, Ámbito y Contexto 31<br />
avengers = -> "Hulk, Thor, Ironman, #{arguments[0]}!"<br />
Aprovecho esta función para recordarte la interpolación por medio<br />
de #{} que aprendiste en el capítulo anterior, acostúmbrate a<br />
utilizarla siempre que puedas. Veamos como quedaría la función<br />
avengers en JavaScript:<br />
avengers = function() {<br />
};<br />
return "Hulk, Thor, Ironman, " + arguments[0] + "!";<br />
3.3 Llamando a funciones<br />
Hasta ahora has aprendido como declarar funciones con CoffeeScript,<br />
sin argumentos, con argumentos y has conocido los splats…<br />
aunque todavía no sabes como llamar a las funciones que hemos<br />
escrito. CoffeeScript vuelve a conseguir de una forma muy simple<br />
que nos centremos únicamente en el hecho y no en la forma:<br />
result = divide 1, 2<br />
Como ves no tenemos que utilizar paréntesis ni punto y coma para<br />
declarar el limite de los argumentos de una función, si compilamos<br />
a JavaScript sería:<br />
var result = divide(1, 2);<br />
Podemos concatenar funciones de una manera sencilla, imagina<br />
que no quieres guardar la suma en una variable result y que<br />
únicamente te interesa lanzar un mensaje por tu pantalla gracias<br />
al método de JavaScript alert:
3. Funciones, Ámbito y Contexto 32<br />
alert divide 1, 2<br />
Fíjate como CoffeeScript sabe perfectamente lo que quieres hacer y<br />
compila un JavaScript lleno de complicadas ( y ):<br />
alert(divide(1, 2));<br />
Voy a complicarlo un poco más, imagínate que tenemos una función<br />
que recibe argumentos combinando valores String, Number,<br />
Boolean con Objects. Veamos la f<br />
hero = (year, properties, superpowers) -><br />
hero 1939, name: "Batman", city: "Gotham", false<br />
Como ves llamo a la función sin delimitar la propia función ni<br />
estableciendo las Curly Braces en el objeto properties, pero CoffeeScript<br />
y su magia vuelven a hacer acto de presencia para que la<br />
función hero funcione sin problemas:<br />
var hero;<br />
hero = function(year, properties, superpowers) {};<br />
hero(1939, {<br />
name: "Batman",<br />
city: "Gotham"<br />
}, false);<br />
Todavía no hemos visto como llamar a una función sin argumentos,<br />
y en esta ocasión se hace igual que en JavaScript utilizando nuestros<br />
queridos (sarcasmo) e inseparables paréntesis:
3. Funciones, Ámbito y Contexto 33<br />
hello()<br />
También existe otra forma de llamar a funciones sin argumentos y<br />
es utilizando el alias operador do, el cual utilizo siempre que puedo<br />
ya que le da más expresividad a mi código:<br />
do hello<br />
Ambos ejemplos, como era de esperar, devuelven el mismo JavaScript:<br />
hello();<br />
3.4 Ámbito<br />
Por ahora no hemos controlado el ciclo de vida de nuestras variables,<br />
el ámbito, por desgracia esta despreocupación en JavaScript<br />
nos puede llevar a futuros problemas dentro de nuestra aplicación<br />
web, menos mal que CoffeeScript nos ayudará en este proceso.<br />
Vamos a ver un sencillo ejemplo:<br />
lifes = 0<br />
restartGame = -> lifes = 3<br />
do restartGame<br />
console.log "I have #{lifes} lifes in the game."<br />
> I have 3 lifes in the game.<br />
Como probablemente esperabas, la consola mostrará I have 3<br />
lifes in the game., pero si realizamos un simple cambio,<br />
conmutando las 2 primeras lineas:
3. Funciones, Ámbito y Contexto 34<br />
restartGame = -> lifes = 3<br />
lifes = 0<br />
do restartGame<br />
console.log "I have #{lifes} lifes in the game."<br />
> I have 0 lifes in the game.<br />
Ahora el resultado, sorprendentemente es totalmente diferente I<br />
have 0 lifes in the game.. Curioso, tu función restartGame no<br />
ha tenido ningún efecto sobre la variable lifes. ¿Cómo ha podido<br />
suceder esto? Te voy a mostrar otro ejemplo a ver si eres capaz de<br />
comprender que está pasando:<br />
restartGame = -> lifes = 3<br />
do restartGame<br />
console.log "I have #{lifes} lifes in the game."<br />
> ReferenceError: lifes is not defined<br />
Hemos conseguido generar un error de ámbito de variable, y ahora<br />
te pregunto ¿como puedes saber el alcance de una determinada<br />
variable? Muy fácil su ámbito se define gracias a tres sencillas<br />
reglas:<br />
• Cada función crea un ámbito, y la única manera de crear un<br />
ámbito es definiendo una función.<br />
• El ciclo de vida de una variable perdurará en el ámbito más<br />
externo en el que se ha hecho una asignación a esa variable.<br />
• Fuera de su ámbito, una variable es invisible.<br />
Tomando como ejemplo los anteriores ejemplos, el ámbito de lifes<br />
en el primer ejemplo fue el contexto global (GlobalScope), en el<br />
segundo ejemplo, hubo una variable lifes en el ámbito global
3. Funciones, Ámbito y Contexto 35<br />
y otra en el ámbito de la función restartGame, y en el último<br />
ejemplo, solo existía dentro de la función restartGame. Es por<br />
eso que tenemos un ReferenceError sobre lifes tratando de ser<br />
utilizada fuera de la función restartGame, puesto como habrás<br />
podido descubrir la variable no existe.<br />
En CoffeeScript al ámbito se conoce como ámbito léxico, y realmente<br />
es el mismo que en JavaScript, salvo que en este último la<br />
declaración del ámbito va asociada a la palabra reservada var y en<br />
CoffeeScript el ámbito se define con la primera asignación. Esto<br />
te ahorra tiempo a la hora de desarrollar tu código, pero siempre<br />
siendo responsable y teniendo cuidado ya que puedes provocar<br />
inconscientemente el llamado shadowing de variables que no es otra<br />
cosa que crear variables fuera de su área de uso.<br />
En CoffeeScript solo existen dos maneras de realizar shadowing<br />
a una variable: la primera la hemos visto en el segundo ejemplo<br />
de restartGame, creando una variable junto con otra variable de<br />
ámbito más interno. La otra manera es como argumento de una<br />
función:<br />
lifes = 3<br />
insertCoin = (coins, lifes) -> lifes += coins * 3<br />
console.log insertCoin 3, lifes<br />
> 12<br />
console.log lifes<br />
> 3<br />
Bien vamos a analizar que está pasando con la función insertCoin,<br />
posiblemente pensabas que por cada coin introducida tu vida se iba<br />
a multiplicar por 3 el numero de lifes de tu variable más externa.<br />
Pero has visto que al consultar lifes ves que sigue siendo 3 y no<br />
12 como esperabas. Realmente la respuesta es sencilla, la función<br />
insertCoin genera un nuevo ámbito para lifes puesto que se pasa
3. Funciones, Ámbito y Contexto 36<br />
como parámetro y el cometido de esta operación es únicamente<br />
devolver el resultado de la operación, el cual no se asigna a lifes.<br />
El shadowing normalmente esta considerado un mala praxis en<br />
programación JavaScript y por lo tanto debes intentar evitarlo.<br />
Intenta dar nombres diferentes a tus variables, para no llegar a<br />
confusiones sobre el ámbito de las mismas.<br />
En este punto te preguntarás, “¿Cómo puedo asignar a una variable<br />
el ámbito correcto sin realizar una asignación?” La respuesta es que<br />
en CoffeeScript es imposible. En lugar de eso tendrás que realizar<br />
una asignación tradicional como por ejemplo usando un null o<br />
cualquier otro valor inicial no sensible. He aquí un ejemplo:<br />
hero = null<br />
window.onload = -> hero = "Batman"<br />
3.5 Contexto<br />
Ámbito y contexto están muy relacionados dentro de CoffeeScript,<br />
pero no por ello deben mezclarse los conceptos. Mientras que el<br />
ámbito se preocupa de la variable y el identificador al que hace<br />
referencia, el contexto se preocupa de la palabra reservada this,<br />
y en el caso de CoffeeScript de su alias @.<br />
Los recién llegados al mundo de JavaScript y CoffeeScript a menudo<br />
se encuentran con un desconcertante y poco descriptivo this. Utilizándolo<br />
correctamente, te sientes verdaderamente un verdadero<br />
SuperHeroe dispuesto a salvar al mundo. Usándolo erróneamente,<br />
puede ser sin lugar a duda un enorme foco de errores. Parte de la<br />
confusión deriva en la palabra misma, la gente espera que this hace<br />
referencia a “este objeto”. En su lugar, debes pensar en ello como<br />
“este contexto” y como verás más adelante, el contexto, this/@,<br />
puede ser totalmente diferente cada vez que se llame a una función.<br />
Para los ejemplos utilizaremos un simple método createPower:
3. Funciones, Ámbito y Contexto 37<br />
createPower = (power) -> @power = power<br />
En este caso @power y power son variables totalmente diferentes,<br />
power, que podrías haberla llamado como quisieses, es una variable<br />
local y nunca se verá fuera de la función createPower, mientras que<br />
@power es una propiedad del contexto.<br />
El objetivo principal de contexto es dar a los métodos de un objeto<br />
(en funciones añadidas como propiedades) una forma de referirse<br />
al objeto que está siendo llamado. Veamos otro ejemplo:<br />
hero = {}<br />
hero.createPower = createPower<br />
hero.createPower "Fly"<br />
console.log hero.power<br />
> "Fly"<br />
Cuando llamamos a hero.createPower, realmente estamos llamando<br />
al método createPower que habíamos creado antes con el objeto<br />
hero como contexto; por lo que @ en la función se refiere al objeto<br />
hero, por lo que creará un atributo @name haciendo referencia a<br />
hero.name. La función en si no ha cambiado, y podríamos llamarla<br />
las veces que necesitemos no cambiando por ello el objeto Hero:<br />
createPower "Fury"<br />
console.log hero.power<br />
> "Fly"<br />
Otro ejemplo que podemos realizar es no añadir la función a ningún<br />
objeto en particular y utilizar los métodos call o apply propios de<br />
la función (y qué como sabes todas las funciones JavaScript tienen).<br />
El método apply necesita un contexto y un array de argumentos<br />
para ejecutarse:
3. Funciones, Ámbito y Contexto 38<br />
superman = {}<br />
createPower superman, ["Fly"]<br />
console.log superman.power<br />
> "Fly"<br />
El método call funciona exactamente igual, excepto por que recibe<br />
un solo argumento y no un array. De esta manera apply es mucho<br />
más versátil puesto que puede recibir una lista de argumentos, de<br />
todas formas veamos el equivalente call al ejemplo anterior:<br />
createPower.call superman, "Fly"<br />
Por ultimo veremos otra manera de dar a una función un contexto<br />
y es utilizando la palabra reservada new, el cual creara un nuevo<br />
objeto utilizando la función como constructor:<br />
Power = createPower<br />
heroes[1] = new Power "Fly"<br />
heroes[2] = new Power "Fury"<br />
console.log heroes[1].power<br />
> ”Fly”<br />
console.log heroes[2].power<br />
> "Fury"<br />
Como te estarás dando cuenta la palabra new no está devolviendo<br />
el resultado de la función createPower, en cambio crea un nuevo<br />
objeto, ejecuta la función en el contexto del objeto y tras ello<br />
devuelve el objeto en si. Ahora ya has alcanzado un nuevo nivel<br />
como desarrollador CoffeeScript, vale la pena que repasemos juntos<br />
lo aprendido:<br />
• Cuando utilizas la palabra new delante de una llamada a una<br />
función, el contexto es el nuevo objeto.
3. Funciones, Ámbito y Contexto 39<br />
• Cuando ejecutas una función mediante call o apply, el<br />
contexto es el primer argumento.<br />
• Si una función se llama como una propiedad de objeto<br />
obj.func o obj['func'], se ejecuta en el contexto de ese<br />
objeto.<br />
• Si no es ninguna de las reglas anteriores, la función se ejecuta<br />
en el contexto global.<br />
3.6 Cambio de contexto en funciones<br />
Los cambios de contexto son algo común en JavaScript, especialmente<br />
en los callbacks de eventos, por lo qué CoffeeScript nos<br />
brinda la posibilidad de manejar esta situación muy facilmente.<br />
Lo más común dentro de una función es la variación de -> por la<br />
llamada Flecha Ancha o RocketArrow =>.<br />
Usando => en vez de la Flecha Fina -> te aseguras que el contexto<br />
de la función será el local. Por ejemplo:<br />
el.addEventListener "click", (event) => @handler event<br />
La razón por la que debes utilizar =>, dejando que CoffeeScript<br />
haga todo el trabajo por nosotros, es para que el callback de<br />
addEventListener no se ejecute en el contexto de el y se ejecute<br />
en el contexto local donde esta declarada la sentencia.<br />
Como hace mucho que no ves código JavaScript, te voy a enseñar<br />
la compilación de este ejemplo para que veas que es lo que hace<br />
CoffeeScript:
3. Funciones, Ámbito y Contexto 40<br />
var _this = this;<br />
el.addEventListener("click", function(event) {<br />
});<br />
return _this.handler(event);<br />
Como ves el truco está en crear una variable _this que hace referencia<br />
al @ global, y por lo tanto es accesible desde addEventListener.<br />
En cambio si utilizamos -> el contexto es el de el y no podríamos<br />
acceder a la función handler:<br />
el.addEventListener "click", (event) -> @handler event<br />
el.trigger "click"<br />
> ReferenceError: handler is not defined<br />
Comprueba que en JavaScript ya no existe la variable this:<br />
el.addEventListener("click", function(event) {<br />
return this.handler(event);<br />
});<br />
el.trigger("click");<br />
En este punto terminamos este capítulo, en mi opinión junto con<br />
las Clases (que veremos en un capítulo futuro) el tratamiento de<br />
Funciones es el area más importante de CoffeeScript. Espero que<br />
haya sido de tu agrado y que ahora te sientas capaz de dominar las<br />
funciones en CoffeeScript como nunca lo habías imaginado.
4. Objetos y Arrays<br />
Ya dominas las funciones con CoffeeScript, ahora toca descubrir<br />
como utilizar tus funciones con colecciones de datos: los objetos y<br />
Arrays.<br />
Comenzaremos echando un vistazo a los objetos como una buena<br />
propuesta para almacenar datos. Tras esto aprenderás como utilizar<br />
las listas, las cuales te darán un buen mecanismo para ordenar nuestros<br />
datos. Desde aquí comenzaremos con las estructuras iterativas<br />
loop, las comprensiones y la creación directa de arrays gracias a<br />
ellas. Selección de matrices en base a matching… y mucho mas.<br />
4.1 Recordando JavaScript y sus Objetos<br />
Antes de comenzar con la sintaxis CoffeeScript, será mejor que<br />
demos un breve repaso a los objetos en JavaScript, reconociendo<br />
que casi todo es un objeto; las únicas excepciones son las primitivas:<br />
boolean, number y string y las constantes undefined y NaN. El<br />
objeto más simple se escribiría:<br />
var object = new Object();<br />
Aunque comúnmente se suele utilizar la sintaxis heredada de JSON²³:<br />
var object = {};<br />
Existen muchísimas otras formas de creación de objetos, es más<br />
en el capítulo anterior hemos estado utilizando algunas de ellas,<br />
²³http://json.org
4. Objetos y Arrays 42<br />
puesto que todas las funciones son en si objetos (puesto que no son<br />
ni boolean, number, string, undefined o NaN).<br />
Para acceder a las propiedades de un objeto tenemos dos formas, con<br />
la notación simple . o con la notación tipo bracket {}. La primera es<br />
sumamente sencilla de utilizar: obj.x hace referencia a la propiedad<br />
x del objeto obj. La notación via bracket es mucho más versátil,<br />
puesto que cualquier expresión que se incluya entre los brackets<br />
se evaluará y converirá a un string utilizándose como nombre de<br />
propiedad, veamos el mismo ejemplo obj['x']<br />
Normalmente, utilizarás la notación por punto si conoces el nombre<br />
de la propiedad previamente y en el caso de que lo tengas que<br />
determinar dinámicamente utilizarás la notación por bracket. Pero<br />
no siempre el primer caso funciona:<br />
symbols.* = 'plus' # Falla!!<br />
symbols.['*'] = 'plus'<br />
La primera sentencia falla ya que + esta reservado como operados,<br />
mientras que la segunda sentencia al ser pasada como string no tiene<br />
ese problema. Creo que por ahora no hace falta que recordemos<br />
más particularidades de JavaScript.<br />
4.2 Objetos<br />
Los objetos en CoffeeScript se pueden crear exactamente igual que<br />
en JavaScript, con nuestras queridas Curly Braces y una lista de<br />
declaraciones de Clave/Valor:<br />
numbers = {one: 1, two: 2}<br />
Sin embargo, al igual que en la invocación de funciones, CoffeeScript<br />
nos permite aligerar nuestro código dejándonos a nuestra
4. Objetos y Arrays 43<br />
elección la utilización de los caracteres {}. Personalmente, las quito<br />
siempre y además utilizo el nivel de indentación con las nuevas<br />
lineas como sustituto del separador , de propiedades:<br />
numbers =<br />
one: 1<br />
two: 2<br />
Con la exclusión completa de {} y , conseguimos que nuestro objeto<br />
sea más parecido a un YAML²⁴:<br />
kids =<br />
brother:<br />
name: "Max"<br />
age: 11<br />
sister:<br />
name: "Ida"<br />
age: 9<br />
Este ejemplo seguro que te hará sonreír ademas de ver el verdadero<br />
potencial de la creación de objetos en una linea:<br />
fellowship = wizard: 'Gandalf', hobbits: ['Frodo', 'Pip\<br />
pin', 'Sam']<br />
La magia de CoffeeScript se encuentra en que cada vez que encuentra<br />
el caracter : sabe que estás haciendo referencia a un objeto.<br />
Esta técnica es especialmente útil cuando una función tiene un<br />
argumento que es un objeto:<br />
²⁴http://yaml.org/
4. Objetos y Arrays 44<br />
findPeople latitude, longitude, order: true<br />
Compilando a JavaScript de la siguiente manera:<br />
drawSprite(latitude, longitude, {order: true});<br />
Aunque ya tienes una base de JavaScript, vale recordar el uso de<br />
this en esta ocasión vamos a crear un objeto que tenga una función<br />
toString que devuelva serializado alguna de las propiedades de<br />
nuestro objeto. Veamos como:<br />
podcast =<br />
number : 11<br />
title : '¿Porqué es difícil testear?'<br />
description: 'Conversación con Javier Acero sobre tes\<br />
tear y programar.'<br />
details:<br />
homepage : 'http://www.bastayadepicar.com'<br />
url : 'http://www.bastayadepicar.com/episodio/\<br />
011'<br />
toString: -> "#{@number}. #{@title}"<br />
Como ves existe el caracter @ que realmente hace referencia a this<br />
que como ya sabemos esta marcando que el contexto es el suyo mismo<br />
(dado que es una flecha fina ->). @ no deja de ser un facilitador<br />
más de CoffeeScript. Una de las cosas que siempre recomiendo a la<br />
hora de trabajar con CoffeeScript es ver el código compilado para<br />
analizar las estructuras JavaScript que genera, analicemos pues:
4. Objetos y Arrays 45<br />
var podcast;<br />
podcast = {<br />
number: 11,<br />
title: '¿Porqué es difícil testear?',<br />
description: 'Conversación con Javier Acero sobre tes\<br />
tear y programar.',<br />
details: {<br />
homepage: 'http://www.bastayadepicar.com',<br />
url: 'http://www.bastayadepicar.com/episodio/011'<br />
},<br />
toString: function() {<br />
return "" + this.number + ". " + this.title;<br />
}<br />
};<br />
En JavaScript, no puedes usar palabras reservadas como atributos<br />
de un objeto, por ejemplo class. CoffeeScript si te permite hacerlo<br />
puesto que todos los accesos a las propiedades los realizará por<br />
notación bracket. Veamos el ejemplo module.class = 'atom' el<br />
cual compilado a JavaScript será module['class'] = 'atom';.<br />
Un último apunte sobre objetos en CoffeeScript, recomiendo encarecidamente<br />
el uso de la expresión of para conocer si una propiedad<br />
existe dentro de un determinado objeto:<br />
console.log wizard of fellowship<br />
4.3 Arrays<br />
Los arrays siguen requiriendo los caracteres [] para indicar sus<br />
elementos, por lo que tampoco vas a encontrar mucha diferencia<br />
con JavaScript. De todas formas puedes utilizar la indentación como<br />
delimitador de elementos y podrás finalizar con , algo que en<br />
JavaScript no está permitido y suele dar bastantes quebraderos de<br />
cabeza. Veamos unos ejemplos:
4. Objetos y Arrays 46<br />
numbers = [1, 2, 3]<br />
letters = [<br />
'A'<br />
'B'<br />
'C'<br />
]<br />
axys = [x, y, z,]<br />
Posiblemente pensarás que no puedo hablar mucho más de Arrays,<br />
nada más lejos de la realidad a partir de ahora voy a poner<br />
casos de uso en combinación con funcionalidades de JavaScript y<br />
CoffeeScript.<br />
Obtener el valor máximo de un array<br />
Imagina que tienes un array numérico y te gustaría obtener el valor<br />
máximo, seguramente ahora estarás pensando en una iteracción en<br />
JavaScript, una variable que va quedándose con el mayor numero<br />
y al final de la iteracción (evidentemente) tendrás el mayor. Bien<br />
vamos a hacerlo al estilo CoffeeScript:<br />
Math.max [12, 32, 11, 67, 1, 3]...<br />
Math.max compara todos los argumentos y devuelve el mayor de<br />
ellos. Para convertir un array a argumentos fíjate que utilizo la<br />
elipsis ..., una técnica muy utilizada para pasar un gran numero<br />
de argumentos a una función.<br />
Mapeando Arrays<br />
Imagina, tienes un array de objetos y quieres crear otro array tomando<br />
ese como base. Lo mismo que el caso anterior, posiblemente<br />
te pondrás a hacer iteraciones, acumuladores… Vamos a ver que<br />
puede hacer CoffeeScript por nosotros:
4. Objetos y Arrays 47<br />
movies = [<br />
name: "Batman", year: 1991, hero: true<br />
,<br />
name: "Spiderman", year: 2003, hero: true<br />
,<br />
name: "Superman", year: 1984, hero: true<br />
,<br />
name: "KickAss", year: 2011, hero: false<br />
]<br />
heroes = movies.map (movie) -> movie.name if movie.hero\<br />
is true<br />
Como ves he utilizado la conocida función map que nos permite<br />
hacer operaciones con los elementos de un array. Como puedes<br />
leer, porque una cosa que nos da CoffeeScript es la comprensión sin<br />
conocimiento, en el nuevo array heroes solo existirán los strings<br />
'Superman', 'Batman' y 'Spiderman' ya que estamos filtrando<br />
por la propiedad hero. Como has podido comprobar tambien he<br />
eliminado las Curly Braces, y he marcado la dimensión de cada<br />
objeto, con un nivel de indentación (fijate que la , esta en un nivel<br />
inferior).<br />
Los mapeos son una buena herramienta para manejar transformaciones<br />
más complicadas que la que hemos realizado, más adelante<br />
conocerás otra manera de hacerlo por medio de comprensiones.<br />
Reduciendo Arrays<br />
Imagina, tenemos un array de elementos y nos gustaría hacer algún<br />
tipo de operación por todos los elementos
4. Objetos y Arrays 48<br />
[1,2,3,4].reduce (x, y) -> x + y<br />
> 10<br />
["batman", "superman", "spiderman", "hulk"].reduceRight\<br />
(x, y) -> x + ", " + y<br />
> 'batman, superman, spiderman, hulk'<br />
También podríamos hacer algo más complejo como generar objetos<br />
tomando como referencia una lista de objetos:<br />
heroes =<br />
{ name: 'batman', year: 1988 }<br />
{ name: 'superman', year: 1981 }<br />
{ name: 'spiderman', year: 2012 }<br />
heroes.reduce (x, y) -><br />
x[y.name]= y.year<br />
x<br />
, {}<br />
> { batman: 1988, spiderman: 1981, spiderman: 2012 }<br />
Los métodos reduce y reduceRight se introdujeron en la versión 1.8<br />
de JavaScript, en este caso CoffeeScript nos provee de una manera<br />
natural y simple para utilizarlos.<br />
Rangos de Arrays<br />
Imagina, que tienes que crear un array que contenga una serie numérica.<br />
Deja de hacer más iteraciones y conoce un nuevo operador<br />
en CoffeeScript:
4. Objetos y Arrays 49<br />
numbers = [1..10]<br />
> [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]<br />
numbers = [10..1]<br />
> [ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]<br />
Cuando escribimos el operador .. CoffeeScript interpreta que necesitamos<br />
una serie numérica entre el primer (1) y el ultimo valor<br />
(10). Si queremos omitir el ultimo valor dentro de la serie tendremos<br />
que utilizar el operador ... como vemos en el siguiente código:<br />
numbers = [1...10]<br />
> [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]<br />
Filtrando Arrays<br />
Imagina, necesitas filtrar un array por una determinada condición<br />
booleana.<br />
numbers = [1..10]<br />
numbers.filter (number) -> number > 5<br />
En el método filter vemos que define como parámetro una función<br />
en la cual estamos pidiendo que se quede con los números<br />
del array mayores que 5 por lo que tendremos el resultado<br />
[6,7,8,9,10].<br />
Uniendo Arrays<br />
Imagina, necesitas unir dos arrays en una sola y no quieres crear un<br />
iterador, mira que sencillo:
4. Objetos y Arrays 50<br />
numbers_1 = [1, 2, 3]<br />
numbers_2 = [4, 5, 6]<br />
numbers = numbers_1.concat numbers_2<br />
> [1, 2, 3, 4, 5, 6]<br />
Realmente concates una función de JavaScript, pero mucha gente<br />
no la utiliza y es realmente util. La gran diferencia con el método<br />
push es que concat crea un nuevo array y deja los arrays que se<br />
tomaron como base sin modificarse.<br />
4.4 Comprensiones<br />
Como hemos visto anteriormente podemos hacer mapeos de funciones<br />
de una manera muy sencilla, pero CoffeeScript nos da las<br />
comprensiones un tipo de operación que los desarrolladores de<br />
Python podrán reconocerlas fácilmente. Vamos a volver a realizar<br />
un mapeo mediante comprensión de nuestra conocida array heroes:<br />
movies = [<br />
name: "Batman", year: 1991, hero: true<br />
,<br />
name: "Spiderman", year: 2003, hero: true<br />
,<br />
name: "Superman", year: 1984, hero: true<br />
,<br />
name: "KickAss", year: 2011, hero: false<br />
]<br />
heroes = (movie.name for movie in movies when movie.her\<br />
o)<br />
> [ 'Doctor Teeth', 'Janice', 'Sgt. Floyd Pepper', 'Zoo\<br />
t', 'Lips', 'Animal' ]<br />
Como puedes comprobar es más sencillo que utilizar el método map<br />
y sobre todo la creación del array heroes es realmente comprensible<br />
(léelo y dime que no).
4. Objetos y Arrays 51<br />
Si todavía no te he convencido vamos a ver otro ejemplo de dos<br />
maneras, la aburrida y la divertida (jedy-mode). Teniendo un array<br />
heroes:<br />
heroes = ['Batman', 'Spiderman', 'Superman']<br />
Muestro por pantalla unos heroes determinados, pero para ello<br />
escribo 3 lineas de código:<br />
for hero, index in heroes<br />
if index % 2 == 0<br />
console.log(hero)<br />
Ahora vas a hacerlo como un verdadero Jedi, sintiendo la fuerza y<br />
elegancia que ofrece CoffeeScript:<br />
console.log hero for hero, index in heroes when index %\<br />
2 is 0<br />
Como vemos, dentro del for podemos además de tener cada elemento<br />
en hero, podemos saber el indice del mismo como segundo<br />
argumento (en este caso index). Con lo que utilizamos este segundo<br />
argumento para hacer la función de filtrado. Ahora ya puedes ir a tu<br />
consola de CoffeeScript para jugar con tus heroes y comprensiones.<br />
Si todavía no estas impresionado por esta capacidad que tiene este<br />
lenguaje, solo me queda sorprendente con una implementación del<br />
mítico FizzBuzz en una única linea:<br />
fizzbuzz = (['fizz' unless i%3] + ['buzz' unless i%5] o\<br />
r i for i in [1..100])<br />
Esta implementación no es mía, proviene del blog de Ricardo<br />
Tomasi²⁵, el cual recomiendo que le eches un vistazo.<br />
²⁵http://ricardo.cc/
5. Clases<br />
Si lo sé, hablar de clases en JavaScript puede ser algo peligroso dado<br />
que algunos puristas, aquellos que vienen de lenguajes tradicionales<br />
y tipados, no toman en serio al lenguaje padre de CoffeeScript. Tal<br />
vez sea porque JavaScript sea el lenguaje más famoso que utiliza el<br />
paradigma basado en prototipos, y los demás como Cecil, Omega,<br />
MOO… no sean tan conocidos. Sin embargo, las clases son tan utiles<br />
en JavaScript como lo son en otros lenguajes, con CoffeeScript lo<br />
que conseguimos es tener un nivel de abstracción mucho mayor.<br />
Desde este capítulo daremos un pequeño repaso a los prototipos y<br />
nos meteremos de lleno en la creación de nuestras primeras clases<br />
con CoffeeScript, utilizando herencia, poliformismo…<br />
5.1 Prototipos<br />
Antes de comenzar con las clases es necesario recordar como<br />
funciona un lenguaje prototípico como JavaScript, un prototipo<br />
es un objeto del cual otros objetos heredan sus propiedades. Por<br />
ejemplo vamos a crear nuestro objeto Hero el cual tendrá un método<br />
prototípico says, veamos como:<br />
Hero = (@power) -><br />
Hero::says = -> console.log "My superpower it's #{@powe\<br />
r}!"<br />
Comenzamos declarando Hero, recuerda que existe una convención<br />
para que las bases tengan la primera letra en mayúscula, el cual<br />
recibe el argumento power en su constructor, con esto conseguimos<br />
que este variable esté disponible en la instancia que creemos de<br />
Hero. Veamos como queda en JavaScript:
5. Clases 53<br />
var Hero;<br />
Hero = function(power) {<br />
};<br />
this.power = power;<br />
Hero.prototype.says = function() {<br />
return console.log("My superpower it's " + this.power\<br />
+ "!");<br />
};<br />
Simplemente por recordar; un constructor es una función, y en<br />
javascript, toda función es un objeto y como cualquier objeto tiene<br />
propiedades y métodos. Una función, por defecto, tiene una serie<br />
de propiedades, como: length, constructor, prototype y algunos<br />
métodos como: call, apply, entre otros. Cualquier función es<br />
susceptible de convertirse en un constructor, basta con anteponer<br />
el operador new a la llamada de la función.<br />
Ahora vamos a crear un par de superheroes, perdón, un par de<br />
instancias Hero estableciendo a cada una de ellas su superpoder y<br />
llamando al método says:<br />
superman = new Hero "fly"<br />
superman.says()<br />
> 'My superpower it's fly!'<br />
batman = new Hero "a belt with gadgets"<br />
batman.says()<br />
> 'My superpower it's a belt with gadgets!'<br />
Como podías prever cada instancia de Hero dice que tiene un poder<br />
distinto porque el método says hace referencia al atributo power de<br />
cada instancia. Evidentemente podemos acceder al atributo power:
5. Clases 54<br />
superman.power<br />
> 'fly'<br />
Cuando usas new, ocurren varias cosas:<br />
• Se crea un nuevo objeto<br />
• El nuevo objeto recibe el prototipo desde el constructor<br />
• Se ejecuta el constructor con el contexto del nuevo objeto<br />
Veamos un ejemplo diferente, mezclando prototipos con estáticos:<br />
Hero = (@power) -><br />
Hero.count++<br />
@number = Hero.count<br />
@says()<br />
Hero.count = 0<br />
Hero::says = -> console.log "#{@number}. My superpower \<br />
it's #{@power}!"<br />
En este caso ampliamos el constructor de Hero, haciendo que tenga<br />
un contador de instancias contenido en la propiedad count la cual<br />
se incrementará cada vez que se crea una nueva instancia (de<br />
ahí que la sentencia no fallará, si posteriormente declaramos esa<br />
variable). Además creamos un atributo de instancia @number que<br />
nos devolverá el valor interno de Hero.count, la última sentencia<br />
del constructor será llamar automáticamente al método prototípico<br />
says demostrando que todo lo creado funciona. El resultado sería:
5. Clases 55<br />
superman = new Hero "fly"<br />
> '1. My superpower it's fly!'<br />
batman = new Hero "a belt with gadgets"<br />
> '2. My superpower it's a belt with gadgets!'<br />
Resumiendo, cada vez que se ejecuta el constructor de Hero, los<br />
pasos son:<br />
• Se asigna el superpoder a la instancia con el shortcut @ dede<br />
el argument<br />
• Se incrementa el contador de instancias count<br />
• Copia el valor a la propiedad @number<br />
• Ejecuta la función heredada @says<br />
Recuerda que todas las funciones del nuevo objeto se ejecutarán en<br />
el contexto del propio objeto, aunque en el capítulo destinado a funciones<br />
ya aprendiste como cambiar los contextos de las funciones<br />
¿no?.<br />
5.2 Clases<br />
Ahora que ya hemos recordado los prototipos puedo decirte que<br />
las clases en CoffeeScript se basan en ellos. Hay varias bibliotecas<br />
que han intentado acercar el mundo de las clases a JavaScript, pero<br />
desde mi humilde punto de vista no les ha ido muy bien ya que<br />
la sintaxis ha quedado demasiado complicada. Es entonces cuando<br />
llega CoffeeScript al rescate y logra simplificar la creación de una<br />
clase. Ahora vamos a crear nuestra primera clase Hero:<br />
class Hero<br />
Como ves la simpleza es máxima y sobre todo entendible y comparable<br />
con otros lenguajes de programación. Vamos a ver como<br />
quedaría en JavaScript:
5. Clases 56<br />
var Hero;<br />
Hero = (function() {<br />
function Hero() {}<br />
return Hero;<br />
})();<br />
Como vemos, únicamente hemos declarado una nueva clase Hero,<br />
y la diferencia entre la carga de sintaxis entre CoffeeScript y<br />
JavaScript es abismal; espero que ahora me entiendas. CoffeeScript<br />
nos proporciona con eficacia una abstracción de la base prototípica,<br />
permitiéndonos escribir código mucho más conciso, y sin perder<br />
ninguno de los beneficios de los objetos en JavaScript. Ahora<br />
creemos un constructor como en el anterior apartado dedicado a<br />
los prototipos:<br />
class Hero<br />
constructor: (@power) -><br />
superman = new Hero "fly"<br />
superman.power<br />
> 'fly'<br />
Compilándose a JavaScript, y viendo claramente lo mucho que<br />
ganas con CoffeeScript:<br />
var Hero;<br />
Hero = (function() {<br />
function Hero(power) {<br />
this.power = power;<br />
}<br />
return Hero;<br />
})();<br />
var superman = new Hero("fly");
5. Clases 57<br />
superman.power;<br />
> 'fly'<br />
Ahora vas a aprender la diferencia en la declaración entre métodos<br />
estáticos y métodos de instancia o prototípicos, para que puedas<br />
hacer comparaciones vamos a continuar con nuestro ejemplo de<br />
Hero:<br />
class Hero<br />
# static private<br />
_count = 0<br />
constructor: (@power) -><br />
_count++<br />
@says()<br />
# Instance methods<br />
says: -><br />
console.log "#{@number()}. My superpower it's #{@po\<br />
wer}!"<br />
number: -> _count<br />
Como vemos lo primero que hago es crear una variable privada<br />
_count que sea accesible para todo el contexto de la clase Hero.<br />
En el constructor, incremento el contador y llamo a la función<br />
de instancia says. ¿Por qué CoffeeScript sabe que el método de<br />
instancia? muy sencillo porque estamos estableciendo el alias @<br />
indicando que el contexto de trabajo es el del constructor, ergo el<br />
de una instancia.<br />
Para declarar métodos de instancia debes utilizar el operador : junto<br />
con el nombre del método que deseas crear. En nuestra clase hemos<br />
creado 2 métodos: says que muestra por pantalla el numero de
5. Clases 58<br />
instancia y el poder de nuestro hero y number que muestra el valor<br />
de la variable _count.<br />
Ahora imagina que necesitas un método estático para la clase Hero,<br />
por ejemplo vamos a crear un método count que nos de el numero<br />
de instancias creadas hasta el momento:<br />
class Hero<br />
# ... previous code ... #<br />
# static public method<br />
@count: -><br />
console.log "Number of instances #{_count}"<br />
De esta manera ahora según vas creando instancias, podrás llamar<br />
al método count de la clase Hero, el cual solo es accesible desde la<br />
clase y no desde sus instancias:<br />
superman = new Hero "fly"<br />
batman = new Hero "a belt with gadgets"<br />
Hero.count()<br />
> 'Number of instances 2'<br />
5.3 Herencia<br />
La implementación de clases de CoffeeScript no estaría completa<br />
sino hubiese ningún mecanismo de obtener herencia entre clases,<br />
tranquilo CoffeeScript la tiene. Vas a poder heredar de otra clase<br />
simplemente usando el operador extends. En el siguiente ejemplo<br />
vas a ver como crear una clase base Vehicle, de la que extenderemos<br />
los vehículos de nuestros heroes. Suena divertido ¿no? comencemos:
5. Clases 59<br />
class Vehicle<br />
fuel: 100<br />
constructor: (@type, @hero) -><br />
use: -><br />
@fuel--<br />
if @fuel > 0<br />
console.log "#{@hero} is using a #{@type}"<br />
else<br />
console.log "Upps!! No fuel in the tank of #{@con\<br />
structor.name}"<br />
Bien, lo primero que he hecho ha sido crear una variable de instancia<br />
fuel, recuerda :, que contendrá el nivel de gasolina del deposito.<br />
El constructor de nuestra clase Vehicle recibe dos argumentos, type<br />
para indicar el tipo de vehículo y heropara indicar el nombre del<br />
heroe dentro del vehículo. Por último he declarado un método de<br />
instancia use el cual irá restando gasolina y testará el nivel de la<br />
misma para dar un mensaje por consola.<br />
Ahora vamos a crear nuestra primera clase extendida de Vehicle,<br />
así que vamos a coger a Batman y le vamos a meter dentro de su<br />
BatMobile:<br />
class BatMobile extends Vehicle<br />
constructor: -><br />
super "car", "Batman"<br />
Como puedes comprobar lo único que hago a la hora de crear la<br />
clase BatMobile es usar el operador extends haciendo referencia<br />
a Vehicle. Tras esto en el constructor de la nueva clase llamo al<br />
constructor de Vehicle con el método super y los 2 argumentos que<br />
necesita "car" haciendo referencia a @type y "Batman" haciendo<br />
referencia a @hero. Tan sencillo como eso, ahora podemos hacer<br />
que Batman use su vehículo:
5. Clases 60<br />
batmobile = new BatMobile()<br />
batmobile.use()<br />
> 'Batman is using a car'<br />
Ahora voy a crear otro vehículo y voy a sobreescribir la variable<br />
fuel en la nueva clase y así podré ver el diferente comportamiento<br />
del método use:<br />
class BatPod extends Vehicle<br />
fuel: 0<br />
constructor: -><br />
super "moto", "Robin"<br />
Pobre "Robin" cuando vaya a utilizar la nueva clase BatPod no va<br />
a poder usarla:<br />
batpod = new BatPod()<br />
batpod.use()<br />
> 'Upps!! No fuel in the tank of BatPod'<br />
Como ves en el método use de la clase Vehicle utilizo el atributo<br />
@constructor.name que hace referencia al nombre de la clase, en<br />
este caso BatPod. Como queremos que Robin pueda disfrutar de<br />
su vehículo vamos a crear un método de instancia y exclusivo de<br />
BatPod para que recargue la gasolina:<br />
class BatPod extends Vehicle<br />
# ... previous code ... #<br />
refuel: -><br />
@fuel = 10
5. Clases 61<br />
Finalizando este apartado tengo que decir que las propiedades<br />
estáticas se copian en las subclases, en lugar de estar heredadas por<br />
referencia. Esto es debido a la arquitectura e implementación de los<br />
prototipos de JavaScript, y desgraciadamente es un problema difícil<br />
de solucionar.<br />
5.4 Polimorfismo<br />
Uno de los usos interesantes de las clases es el llamado polimorfismo,<br />
un concepto de la programación orientada a objetos el cual podría<br />
decirse que en esencia refiere al comportamiento de los objetos, no<br />
a su pertenencia a una jerarquía de clases (o a sus tipos de datos).<br />
Te voy a refrescar este concepto con un ejemplo muy sencillo:<br />
class Vehicle<br />
constructor: (@fuel = 10) -><br />
burnout: -><br />
throw new Error "I'm a abstract method"<br />
En este caso nuestra clase Vehicle tiene un método abstracto<br />
burnout el cual si intentamos llamarlo desde una nueva instancia<br />
de esta clase nos dará una excepción. Este método se utilizará en las<br />
subclases de Vehicle y nos devolverá el numero de kilómetros que<br />
puede recorrer el vehículo al ejecutar el método:
5. Clases 62<br />
class BatMobile extends Vehicle<br />
constructor: -><br />
super fuel = 50<br />
burnout: -><br />
console.log @fuel / 25<br />
class BatPod extends Vehicle<br />
burnout: -><br />
console.log @fuel / 8<br />
La diferencia entre el BatMobile y el BatPod es únicamente que el<br />
primero establece el argumento fuel con una cantidad de 50. Como<br />
puedes ver cada método burnout realiza diferentes operaciones<br />
puesto que el consumo de los vehículos es distinto.<br />
kmInBurnout = (vehicle) -><br />
unless vehicle instanceof Vehicle<br />
throw new Error "vehicle requires a Vehicle instance\<br />
!"<br />
console.log vehicle.burnout()<br />
kmInBurnout new BatMobile()<br />
> 2<br />
kmInBurnout new BatPod()<br />
> 1,25<br />
La función kmInBurnout lo primero que hace al recibir un argumento<br />
es comprobar que sea una instancia de la clase Vehicle, para<br />
ello se utiliza la expresión instanceof. En el caso de que sea una<br />
instancia válida devolverá por consola el resultado de la función<br />
burnout.
6. Modularización<br />
Uno de los grandes problemas que existe con JavaScript, ergo con<br />
CoffeeScript, es que no importa el numero de ficheros que tengas<br />
tu proyecto. JavaScript solo sabe de lineas y por lo tanto existe la<br />
posibilidad de que El Libre Albedrío haga acto de presencia, creando<br />
estructuras incorrectas, inmantenibles y peligrando el GlobalScope.<br />
Se que esto puede darse en todos los lenguajes de programación,<br />
pero bajo mi punto de vista JavaScript Todos estos problemas crecen<br />
exponencialmente cuando es un equipo el que se enfrenta a un proyecto<br />
en JavaScript/CoffeeScript, tenemos/tienes que solucionarlo.<br />
En este capítulo he intentado reunir algunos puntos interesantes<br />
que tienes que tener en cuenta para tener un código organizado y<br />
mantenible; independientemente del patrón de diseño que utilices.<br />
6.1 Namespacing<br />
Como ya vimos en capítulos anteriores, todas las variables que se<br />
declaran en una función solo existen en el ámbito propio de la<br />
función. Cada nuevo fichero que compila CoffeeScript a su vez<br />
genera una función autoejecutable, con su propio ámbito, y por<br />
lo tanto si tenemos dos ficheros las variables de un fichero no<br />
serán visibles desde el otro, a menos que estén declaradas en el<br />
GlobalScope. Por una parte esta bien pensado ya que a menos que lo<br />
queramos hacer consecuentemente, tu GlobalScope quedará limpio<br />
y sin saber de tus mil y una funciones.<br />
Bien, y ahora te pregunto ¿cómo podemos acceder a esas funciones<br />
y variables que tenemos en módulos separados y fuera del GlobalScope?;<br />
muy sencillo. Debemos declarar una única variable global, en<br />
browsers asignarla al objeto window y en proyectos NodeJS al objeto<br />
global. Veamos como:
6. Modularización 64<br />
(global or window).libjs = {}<br />
Tan sencillo como lo que ves, nos aseguramos que nuestro objeto<br />
libjs va a declararse en entornos browser o NodeJS. A partir de<br />
ahora ya podemos crear nuestros diferentes módulos utilizando el<br />
namespace libjs:<br />
math.coffee<br />
libjs.math =<br />
sum : (a, b) -> a + b<br />
rest: (a, b) -> a - b<br />
# ... #<br />
constants.coffee<br />
libjs.CONST =<br />
MIN_VALUE: 10<br />
MAX_VALUE: 90<br />
class.coffee<br />
class libjs.Hero<br />
Este es un primer acercamiento a como tener organizados tus<br />
fuentes creando un sistema de Namespaces, de todas formas, te<br />
recomiendo que junto al Namespacing deberás utilizar algún patrón<br />
de diseño; siempre adecuado a la naturaleza de tu proyecto.<br />
6.2 Mixins<br />
Los llamados mixins²⁶ no están soportados de forma nativa en<br />
CoffeeScript, por la sencilla razón de que son triviales y cada<br />
²⁶http://en.wikipedia.org/wiki/Mixin
6. Modularización 65<br />
desarrollador hace su propia implementación; algo parecido a lo<br />
que pasa con el patrón MVC (sarcasmo). En el siguiente ejemplo<br />
podemos ver un ejemplo:<br />
extend = (obj, mixin) -><br />
obj[name] = method for name, method of mixin obj<br />
include = (class_reference, mixin) -><br />
extend class_reference.prototype, mixin<br />
include Hero, film: true<br />
(new Hero).film<br />
En este ejemplo declaro extend e include, mientras que el primero<br />
se encarga de sobrecargar propiedades estáticas el segundo se ocupa<br />
de propiedades de instancia. En el ejemplo puedes comprobar<br />
como creo una propiedad de instancia film para indicar si nuestro<br />
superheroe ha tenido una pelcula, por defecto true.<br />
Los mixins son un buen patrón para compartir lógica de negocio<br />
común entre diferentes módulos de tu aplicación. La ventaja de los<br />
mixins es que puedes incluirlo en más de un objeto o clase, mientras<br />
que comparándolo con la herencia de una clase solo puede heredar<br />
de un único origen.<br />
6.3 Extendiendo clases<br />
Una cosa que aprendí cuando lei mi primer libro sobre CoffeeScript,<br />
escrito por Alex MacCaw²⁷, fue que los mixins no son suficientes<br />
ya que no estan muy orientados a objetos. Necesitamos una mejor<br />
manera de integrar mixins en nuestras clases CoffeeScript, y Alex<br />
pensó en crear una clase base Module de la que el resto de clases<br />
extendiensen. La transcribo tal cual el lo pensó:<br />
²⁷http://alexmaccaw.com/
6. Modularización 66<br />
KEYWORDS = ['extended', 'included']<br />
class Module<br />
@extend: (obj) -><br />
for key, value of obj when key not in KEYWORDS<br />
@[key] = value<br />
obj.extended?.apply(@)<br />
@<br />
@include: (obj) -><br />
for key, value of obj when key not in KEYWORDS<br />
@::[key] = value<br />
obj.included?.apply(@)<br />
@<br />
Como vemos crea dos funciones estáticas extend e include las<br />
cuales reciben un objeto, que en este caso será una secuencia de<br />
nombres de función y su función. La función extend se encargará<br />
de generar las funciones estáticas y la función include las funciones<br />
de instancia, facil. Ahora vamos a ponerlo en práctica, imagina que<br />
tienes un modulo que genera IDs aleatorios:<br />
libjs.guid.coffee<br />
libjs.guid =<br />
generate: -><br />
'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace /[xy\<br />
]/g, (c) -><br />
r = Math.random() * 16 | 0<br />
v = if c is 'x' then r else r & 3 | 8<br />
v.toString 16<br />
.toUpperCase()<br />
El módulo libjs.guid y su función generate lo vamos a incluir<br />
dentro una nueva clase Hero extendida de Module, y la vamos a
6. Modularización 67<br />
utilizar en el constructor que se encargará de con cada instancia<br />
tenga un atributo id que lo identifica:<br />
class Hero extends Module<br />
@include libjs.guid<br />
constructor: (@name) -><br />
@id = @generate()<br />
console.log "Instance with id #{@id}"<br />
Parece sencillo no?, vamos a ponerlo en práctica creando un par de<br />
instancias:<br />
batman = new Hero "Batman"<br />
> 'Instance with id 04C1E095-E1B9-475F-9D8D-48BD931AAD0\<br />
4'<br />
superman = new Hero "Superman"<br />
> 'Instance with id 1EC75599-6CA7-490B-B3C1-F68CF468881\<br />
4'<br />
Como vemos la clase Module que pensó Alex McCaw es tremendamente<br />
fácil de implementar y extensible horizontalmente ya que<br />
puedes tener módulos iguales que se incluyen en clases diferentes,<br />
generando un código así mucho más mantenible. Por ponerte un<br />
ejemplo mis proyectos Monocle²⁸ y Atoms²⁹ utilizan la class Module<br />
y utilizan módulos comunes, a pesar de ser proyectos diferentes.<br />
²⁸https://github.com/soyjavi/monocle<br />
²⁹https://github.com/tapquo/atoms
7. Bibliografía<br />
En este capítulo intento reunir toda la documentación que un<br />
CoffeeScripter tiene que leer para convertirse en un mejor desarrollador.<br />
Muchos de los libros que aparecen en esta lista me han<br />
ayudado a escribir este libro y por lo tanto me siento con el<br />
compromiso de obligarte a leerlos a ti también.<br />
• JavaScript: The Good Partspor Douglas CrockFord (Oreilly)<br />
- Comprar³⁰<br />
• Eloquent JavaScriptpor Marijn Haverbeke - Descargar³¹<br />
• The little book of CoffeeScriptpor Alex McCaw (Oreilly) -<br />
Comprar³²<br />
• CoffeeScript: Accelerated JavaScript Developmentpor Trevor<br />
Burnham (The Pragmatic Programmers) - Comprar³³<br />
• CoffeeScript Cookbookpor David Brady & Co. - Online³⁴<br />
• Testing with CoffeeScriptpor Jack Franklin - Descargar³⁵<br />
• Clean Codepor Robert C. Martin (Prentice Hall) - Comprar³⁶<br />
³⁰http://shop.oreilly.com/product/9780596517748.do<br />
³¹http://eloquentjavascript.net/<br />
³²http://shop.oreilly.com/product/0636920024309.do<br />
³³http://pragprog.com/book/tbcoffee/<strong>coffeescript</strong><br />
³⁴http://<strong>coffeescript</strong>cookbook.com/<br />
³⁵https://efendibooks.com/minibooks/testing-with-<strong>coffeescript</strong><br />
³⁶http://www.amazon.es/Clean-Code-Handbook-Software-Craftsmanship/dp/<br />
0132350882