Documentos de Académico
Documentos de Profesional
Documentos de Cultura
Coffeescript PDF
Coffeescript PDF
Javi Jimenez
Este libro está a la venta en http://leanpub.com/coffeescript
Agradecimientos . . . . . . . . . . . . . . . . . . . . . . . . 1
Prefacio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
JavaScript, El lenguaje padre . . . . . . . . . . . . . . . . 3
CoffeeScript, El hijo bastardo . . . . . . . . . . . . . . . 4
Un libro por el mundo . . . . . . . . . . . . . . . . . . . 5
1. Comenzando . . . . . . . . . . . . . . . . . . . . . . . . 7
1.1 Entorno necesario . . . . . . . . . . . . . . . . . . . . 7
1.2 Instalando NodeJS . . . . . . . . . . . . . . . . . . . . 8
1.3 Instalando CoffeeScript . . . . . . . . . . . . . . . . . 11
2. Sintaxis . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
2.1 Valores, variables y comentarios . . . . . . . . . . . . 13
2.2 Interpolación de cadenas . . . . . . . . . . . . . . . . 15
2.3 Control de flujo . . . . . . . . . . . . . . . . . . . . . 16
2.4 Loops . . . . . . . . . . . . . . . . . . . . . . . . . . 18
2.5 Alias y Operadores . . . . . . . . . . . . . . . . . . . 22
4. Objetos y Arrays . . . . . . . . . . . . . . . . . . . . . . 41
4.1 Recordando JavaScript y sus Objetos . . . . . . . . . . 41
4.2 Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . 42
4.3 Arrays . . . . . . . . . . . . . . . . . . . . . . . . . . 45
4.4 Comprensiones . . . . . . . . . . . . . . . . . . . . . 50
5. Clases . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
5.1 Prototipos . . . . . . . . . . . . . . . . . . . . . . . . 52
5.2 Clases . . . . . . . . . . . . . . . . . . . . . . . . . . 55
5.3 Herencia . . . . . . . . . . . . . . . . . . . . . . . . . 58
5.4 Polimorfismo . . . . . . . . . . . . . . . . . . . . . . 61
6. Modularización . . . . . . . . . . . . . . . . . . . . . . . 63
6.1 Namespacing . . . . . . . . . . . . . . . . . . . . . . 63
6.2 Mixins . . . . . . . . . . . . . . . . . . . . . . . . . . 64
6.3 Extendiendo clases . . . . . . . . . . . . . . . . . . . 65
7. Bibliografía . . . . . . . . . . . . . . . . . . . . . . . . . 68
Agradecimientos
Escribir un libro en estos tiempos tan sumamente acelerados supone
un gran esfuerzo. Principalmente necesitas tiempo y normalmente
la gente no está dispuesta a dártelo; es por eso que doy las gracias
a todas esas personas que me han permitido gastar mi tiempo en lo
que hoy considero importante.
Dar las gracias a mi familia por siempre haberme permitido tener
la libertad que he necesitado para poder equivocarme, conocerme
y ser la persona que hoy soy.
Dar las gracias al equipo de Tapquo¹: Ina, Cata, Oihane, Janire y
Joseba por continuar con el día a día de nuestra loca empresa y
seguir con el propósito que nos hemos marcado juntos.
Dar las gracias a accionistas, consejeros y amigos de Tapquo por
confiar en mi y darme la libertad para escaparme del país y
centrarme en este texto que ahora lees.
Dar las gracias también a Jeremy Ashkenas por crear el lenguaje
CoffeeScript y contribuir altruistamente a la evolución del mundo
web, sin olvidar a los numerosos contribuyentes al proyecto, y a
otros proyectos OpenSource, que por suerte son demasiados para
nombrarlos uno a uno aquí.
Por último quiero darte las gracias a ti, gracias por descargarte
este libro y darme una oportunidad de contarte lo que ha supuesto
CoffeeScript en mi vida. Lo mucho que nos ha ayudado dentro de
Tapquo creando un mejor código, más mantenible y más comprensi-
ble. Ahora solo te pido una última cosa, comparte este libro cómo lo
he hecho yo contigo, regálalo y ayúdame a transmitir este contenido
por el mundo.
¹http://tapquo.com
Prefacio
Tal vez no sabes muy bien porqué estás leyendo este pequeño libro
sobre CoffeeScript, antes de nada piensa en este libro como un
regalo que te ofrezco, no espero nada a cambio, únicamente que
disfrutes con su lectura tanto como yo he disfrutado escribiéndolo.
Te preguntarás por qué he decidido escribir un libro en castellano
sobre CoffeeScript, y no sobre JavaScript como muchos esperaban
que hiciera. Razones tengo muchas pero la principal es que tal
vez sea el lenguaje con el que más me he divertido mientras lo
descubría y el que más me ha ayudado a mejorar mis capacidades
como desarrollador web. No te puedes hacer una idea lo que ha
supuesto CoffeeScript para mi y por lo tanto para la empresa que
fundé, Tapquo². Quiero contarte mis inicios con este maravilloso
lenguaje y también su propia génesis, comencemos.
La primera vez que escuché de CoffeeScript fue gracias a mi amigo
Guillermo Pascual³ cuando en Septiembre del 2011 estábamos en la
primera oficina de Tapquo desarrollando, y refactorizando, lo que
iba a ser la primera versión de LungoJS⁴. Al principio no le presté
mucha atención a CoffeeScript, pretendía ser mejor programador
en JavaScript y Lungo iba a ser mi primer gran exponente, tenia
puesto el Focus y no debía dispersarme con eso que nos gusta tanto
a los Developers; aprender un nuevo lenguaje.
Fue en Junio del 2012, una vez que Lungo ya era estable y yo ya
dominaba JavaScript como pretendía, cuando dediqué mi tiempo
a CoffeeScript. Comencé leyendo varios libros, tampoco había
muchos más, los cuales me han ayudado a escribir el libro que
²http://tapquo
³http://twitter.com/pasku1
⁴http://lungo.tapquo.com
Prefacio 3
Este libro fue escrito durante mi viaje por Tailandia, en los meses
de Noviembre y Diciembre del año 2013. Decidí irme a 12.000km
de mi lugar de residencia para centrarme en el libro y poder dar
lo mejor de mi en él. Uno de mis propósitos en la vida es ofrecer
⁹https://en.wikipedia.org/wiki/Jeremy_Ashkenas
¹⁰https://github.com/jashkenas/coffee-script
¹¹https://en.wikipedia.org/wiki/David_Heinemeier_Hansson
Prefacio 6
Mac
• Instala Xcode
• Instala GIT
• Ejecuta los siguientes comandos:
¹⁶http://coffeescript.org
¹⁷http://brew.sh/
1. Comenzando 9
darwin_setup.sh
git clone git://github.com/ry/node.git
cd node
./configure
make
sudo make install
Linux (Ubuntu)
ubuntu_setup.sh
git clone git://github.com/ry/node.git
cd node
./configure
make
sudo make install
Windows
node -v
Ahora viendo que tienes una versión reciente (por tu bien) vas a
crear un pequeño servidor con un único fichero, a este le llamaras
hello_node.js:
hello_node.js
node hello_node.js
coffee -v
coffee
1. Comenzando 12
¹⁹http://www.amazon.com/JavaScript-Good-Parts-Douglas-Crockford/dp/0596517742
²⁰http://eloquentjavascript.net/
2. Sintaxis 14
heroe = "Superman"
year = 1984
crockford[3] = """
JavaScript doesn't suck!
You're just doing it wrong."""
# Tu primer comentario
###
Un comentario multilinea,
por ejemplo definir la licencia
o autor del código fuente.
###
hero = "Batman..."
chat = """
friend : What... is your favorite hero?
soyjavi: #{hero}
friend : Seriously?"""
if year == 1980
born = true
else
born = false
var born;
if (year === 1980) {
born = true;
} else {
born = false;
}
var born;
if (year !== 1980) {
born = false;
}
Ya hemos visto los alias is, unless y isnt más adelante podrás
conocer más alias disponibles en CoffeeScript los cuales aportarán
una mayor expresividad a tu código.
2.4 Loops
heroes = 0
while heroes <= 12
heroes++
heroes = 0
while heroes <= 12 then heroes++
heroes = 0
until heroes is 12 then heroes++
²¹https://en.wikipedia.org/wiki/Robert_Cecil_Martin
2. Sintaxis 20
Al igual que con las estructuras if, while, until podemos resolver
el anterior ejemplo en una única y expresiva linea:
heroes = 2
loop
break if heroes is 8
heroes++
console.log heroes
var heroes = 2;
while (true) {
if (heroes === 8 {
break;
}
heroes++;
}
console.log(heroes);
facebook_account = false
if not facebook_account
console.log "Well done!!"
facebook_account = false
twitter_account = false
if not facebook_account and not twitter_account
console.log "You're a caveman."
var where;
where = typeof location !== "undefined" && location !==\
null ? location : "Gotham";
batman.vehicles?.batMobile()
var _ref;
if ((_ref = batman.vehicles) != null) {
_ref.batMobile();
}
goku.movements?.kamehame?()
var _ref;
if ((_ref = goku.movements) != null) {
if (typeof _ref.kamehame === "function") {
_ref.kamehame();
}
}
@twitter = "@soyjavi"
2. Sintaxis 25
Goku::life = 10
3.1 Funciones
Como vale más un ejemplo que mil palabras, con la siguiente linea
te descubro tu primera función en CoffeeScript:
hello = ->
# ... amazing CoffeeScript code!
“world”
En esta ocasión tenemos una función divide que recibe dos argu-
mentos a y b los cuales se dividen entre si y se devuelve el resultado.
Esto mismo compilado en JavaScript:
²²http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/
0132350882
3. Funciones, Ámbito y Contexto 28
var divide;
divide = function(a, b) {
if (b == null) {
b = 1;
}
return a / b;
};
var divide,
__slice = [].slice;
divide = function() {
var numbers, result;
numbers = 1 <= arguments.length ? __slice.call(argume\
nts, 0) : [];
result = 0;
numbers.forEach(function(number) {
return result = result / number;
});
return result;
};
var race,
__slice = [].slice;
race = function() {
var runners, winner;
winner = arguments[0], runners = 2 <= arguments.lengt\
h ? __slice.call(arguments, 1) : [];
return console.log(winner, runners.join(“, “));
};
Los splats van mucho más allá, veamos como crear un sandwich
combinando argumentos:
avengers = function() {
return "Hulk, Thor, Ironman, " + arguments[0] + "!";
};
result = divide 1, 2
alert divide 1, 2
alert(divide(1, 2));
var hero;
hero(1939, {
name: "Batman",
city: "Gotham"
}, false);
hello()
do hello
hello();
3.4 Ámbito
lifes = 0
restartGame = -> lifes = 3
do restartGame
lifes = 3
insertCoin = (coins, lifes) -> lifes += coins * 3
console.log insertCoin 3, lifes
> 12
console.log lifes
> 3
hero = null
3.5 Contexto
hero = {}
hero.createPower = createPower
hero.createPower "Fly"
console.log hero.power
> "Fly"
createPower "Fury"
console.log hero.power
> "Fly"
superman = {}
createPower superman, ["Fly"]
console.log superman.power
> "Fly"
Power = createPower
heroes[1] = new Power "Fly"
heroes[2] = new Power "Fury"
console.log heroes[1].power
> ”Fly”
console.log heroes[2].power
> "Fury"
el.addEventListener("click", function(event) {
return _this.handler(event);
});
Como ves el truco está en crear una variable _this que hace referen-
cia al @ global, y por lo tanto es accesible desde addEventListener.
En cambio si utilizamos -> el contexto es el de el y no podríamos
acceder a la función handler:
el.addEventListener("click", function(event) {
return this.handler(event);
});
el.trigger("click");
puesto que todas las funciones son en si objetos (puesto que no son
ni boolean, number, string, undefined o NaN ).
Para acceder a las propiedades de un objeto tenemos dos formas, con
la notación simple . o con la notación tipo bracket {}. La primera es
sumamente sencilla de utilizar: obj.x hace referencia a la propiedad
x del objeto obj. La notación via bracket es mucho más versátil,
puesto que cualquier expresión que se incluya entre los brackets
se evaluará y converirá a un string utilizándose como nombre de
propiedad, veamos el mismo ejemplo obj['x']
Normalmente, utilizarás la notación por punto si conoces el nombre
de la propiedad previamente y en el caso de que lo tengas que
determinar dinámicamente utilizarás la notación por bracket. Pero
no siempre el primer caso funciona:
4.2 Objetos
numbers =
one: 1
two: 2
kids =
brother:
name: "Max"
age: 11
sister:
name: "Ida"
age: 9
²⁴http://yaml.org/
4. Objetos y Arrays 44
podcast =
number : 11
title : '¿Porqué es difícil testear?'
description: 'Conversación con Javier Acero sobre tes\
tear y programar.'
details:
homepage : 'http://www.bastayadepicar.com'
url : 'http://www.bastayadepicar.com/episodio/\
011'
toString: -> "#{@number}. #{@title}"
var podcast;
podcast = {
number: 11,
title: '¿Porqué es difícil testear?',
description: 'Conversación con Javier Acero sobre tes\
tear y programar.',
details: {
homepage: 'http://www.bastayadepicar.com',
url: 'http://www.bastayadepicar.com/episodio/011'
},
toString: function() {
return "" + this.number + ". " + this.title;
}
};
4.3 Arrays
numbers = [1, 2, 3]
letters = [
'A'
'B'
'C'
]
axys = [x, y, z,]
Mapeando Arrays
movies = [
name: "Batman", year: 1991, hero: true
,
name: "Spiderman", year: 2003, hero: true
,
name: "Superman", year: 1984, hero: true
,
name: "KickAss", year: 2011, hero: false
]
heroes = movies.map (movie) -> movie.name if movie.hero\
is true
Reduciendo Arrays
heroes =
{ name: 'batman', year: 1988 }
{ name: 'superman', year: 1981 }
{ name: 'spiderman', year: 2012 }
Rangos de Arrays
Imagina, que tienes que crear un array que contenga una serie nu-
mérica. Deja de hacer más iteraciones y conoce un nuevo operador
en CoffeeScript:
4. Objetos y Arrays 49
numbers = [1..10]
> [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ]
numbers = [10..1]
> [ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1 ]
numbers = [1...10]
> [ 1, 2, 3, 4, 5, 6, 7, 8, 9 ]
Filtrando Arrays
numbers = [1..10]
numbers.filter (number) -> number > 5
Uniendo Arrays
numbers_1 = [1, 2, 3]
numbers_2 = [4, 5, 6]
numbers = numbers_1.concat numbers_2
> [1, 2, 3, 4, 5, 6]
4.4 Comprensiones
Como hemos visto anteriormente podemos hacer mapeos de fun-
ciones de una manera muy sencilla, pero CoffeeScript nos da las
comprensiones un tipo de operación que los desarrolladores de
Python podrán reconocerlas fácilmente. Vamos a volver a realizar
un mapeo mediante comprensión de nuestra conocida array heroes:
movies = [
name: "Batman", year: 1991, hero: true
,
name: "Spiderman", year: 2003, hero: true
,
name: "Superman", year: 1984, hero: true
,
name: "KickAss", year: 2011, hero: false
]
heroes = (movie.name for movie in movies when movie.her\
o)
> [ 'Doctor Teeth', 'Janice', 'Sgt. Floyd Pepper', 'Zoo\
t', 'Lips', 'Animal' ]
Como vemos, dentro del for podemos además de tener cada ele-
mento en hero, podemos saber el indice del mismo como segundo
argumento (en este caso index). Con lo que utilizamos este segundo
argumento para hacer la función de filtrado. Ahora ya puedes ir a tu
consola de CoffeeScript para jugar con tus heroes y comprensiones.
Si todavía no estas impresionado por esta capacidad que tiene este
lenguaje, solo me queda sorprendente con una implementación del
mítico FizzBuzz en una única linea:
5.1 Prototipos
var Hero;
Hero = function(power) {
this.power = power;
};
Hero.prototype.says = function() {
return console.log("My superpower it's " + this.power\
+ "!");
};
Como podías prever cada instancia de Hero dice que tiene un poder
distinto porque el método says hace referencia al atributo power de
cada instancia. Evidentemente podemos acceder al atributo power:
5. Clases 54
superman.power
> 'fly'
Hero.count = 0
Hero::says = -> console.log "#{@number}. My superpower \
it's #{@power}!"
5.2 Clases
Ahora que ya hemos recordado los prototipos puedo decirte que
las clases en CoffeeScript se basan en ellos. Hay varias bibliotecas
que han intentado acercar el mundo de las clases a JavaScript, pero
desde mi humilde punto de vista no les ha ido muy bien ya que
la sintaxis ha quedado demasiado complicada. Es entonces cuando
llega CoffeeScript al rescate y logra simplificar la creación de una
clase. Ahora vamos a crear nuestra primera clase Hero:
class Hero
var Hero;
Hero = (function() {
function Hero() {}
return Hero;
})();
class Hero
constructor: (@power) ->
var Hero;
Hero = (function() {
function Hero(power) {
this.power = power;
}
return Hero;
})();
superman.power;
> 'fly'
class Hero
# static private
_count = 0
# Instance methods
says: ->
console.log "#{@number()}. My superpower it's #{@po\
wer}!"
class Hero
# ... previous code ... #
Hero.count()
> 'Number of instances 2'
5.3 Herencia
class Vehicle
fuel: 100
use: ->
@fuel--
if @fuel > 0
console.log "#{@hero} is using a #{@type}"
else
console.log "Upps!! No fuel in the tank of #{@con\
structor.name}"
constructor: ->
super "moto", "Robin"
refuel: ->
@fuel = 10
5. Clases 61
5.4 Polimorfismo
class Vehicle
constructor: (@fuel = 10) ->
burnout: ->
throw new Error "I'm a abstract method"
6.1 Namespacing
Como ya vimos en capítulos anteriores, todas las variables que se
declaran en una función solo existen en el ámbito propio de la
función. Cada nuevo fichero que compila CoffeeScript a su vez
genera una función autoejecutable, con su propio ámbito, y por
lo tanto si tenemos dos ficheros las variables de un fichero no
serán visibles desde el otro, a menos que estén declaradas en el
GlobalScope. Por una parte esta bien pensado ya que a menos que lo
queramos hacer consecuentemente, tu GlobalScope quedará limpio
y sin saber de tus mil y una funciones.
Bien, y ahora te pregunto ¿cómo podemos acceder a esas funciones
y variables que tenemos en módulos separados y fuera del GlobalS-
cope?; muy sencillo. Debemos declarar una única variable global, en
browsers asignarla al objeto window y en proyectos NodeJS al objeto
global. Veamos como:
6. Modularización 64
(global or window).libjs = {}
Tan sencillo como lo que ves, nos aseguramos que nuestro objeto
libjs va a declararse en entornos browser o NodeJS. A partir de
ahora ya podemos crear nuestros diferentes módulos utilizando el
namespace libjs:
math.coffee
libjs.math =
sum : (a, b) -> a + b
rest: (a, b) -> a - b
# ... #
constants.coffee
libjs.CONST =
MIN_VALUE: 10
MAX_VALUE: 90
class.coffee
class libjs.Hero
6.2 Mixins
Una cosa que aprendí cuando lei mi primer libro sobre CoffeeScript,
escrito por Alex MacCaw²⁷, fue que los mixins no son suficientes
ya que no estan muy orientados a objetos. Necesitamos una mejor
manera de integrar mixins en nuestras clases CoffeeScript, y Alex
pensó en crear una clase base Module de la que el resto de clases
extendiensen. La transcribo tal cual el lo pensó:
²⁷http://alexmaccaw.com/
6. Modularización 66
class Module
@extend: (obj) ->
for key, value of obj when key not in KEYWORDS
@[key] = value
obj.extended?.apply(@)
@
libjs.guid =
generate: ->
'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace /[xy\
]/g, (c) ->
r = Math.random() * 16 | 0
v = if c is 'x' then r else r & 3 | 8
v.toString 16
.toUpperCase()
³⁰http://shop.oreilly.com/product/9780596517748.do
³¹http://eloquentjavascript.net/
³²http://shop.oreilly.com/product/0636920024309.do
³³http://pragprog.com/book/tbcoffee/coffeescript
³⁴http://coffeescriptcookbook.com/
³⁵https://efendibooks.com/minibooks/testing-with-coffeescript
³⁶http://www.amazon.es/Clean-Code-Handbook-Software-Craftsmanship/dp/
0132350882