Documente Academic
Documente Profesional
Documente Cultură
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 Interpolacin 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. Modularizacin . . . . . . . . . . . . . . . . . . . . . . . 63
6.1 Namespacing . . . . . . . . . . . . . . . . . . . . . . 63
6.2 Mixins . . . . . . . . . . . . . . . . . . . . . . . . . . 64
6.3 Extendiendo clases . . . . . . . . . . . . . . . . . . . 65
7. Bibliografa . . . . . . . . . . . . . . . . . . . . . . . . . 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 drtelo; 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 da a da de nuestra loca empresa y
seguir con el propsito 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 pas y
centrarme en este texto que ahora lees.
Dar las gracias tambin a Jeremy Ashkenas por crear el lenguaje
CoffeeScript y contribuir altruistamente a la evolucin 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 cdigo, ms mantenible y ms comprensi-
ble. Ahora solo te pido una ltima cosa, comparte este libro cmo lo
he hecho yo contigo, reglalo y aydame a transmitir este contenido
por el mundo.
http://tapquo.com
Prefacio
Tal vez no sabes muy bien porqu ests leyendo este pequeo 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 escribindolo.
Te preguntars 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 ms me he divertido mientras lo
descubra y el que ms 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 tambin su propia gnesis, comencemos.
La primera vez que escuch de CoffeeScript fue gracias a mi amigo
Guillermo Pascual cuando en Septiembre del 2011 estbamos en la
primera oficina de Tapquo desarrollando, y refactorizando, lo que
iba a ser la primera versin de LungoJS. Al principio no le prest
mucha atencin a CoffeeScript, pretenda ser mejor programador
en JavaScript y Lungo iba a ser mi primer gran exponente, tenia
puesto el Focus y no deba 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 pretenda, cuando dediqu mi tiempo
a CoffeeScript. Comenc leyendo varios libros, tampoco haba
muchos ms, 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 ao 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 propsitos 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 versin reciente (por tu bien) vas a
crear un pequeo 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 cdigo 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;
}
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
hello = ->
# ... amazing CoffeeScript code!
world
En esta ocasin tenemos una funcin 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(, ));
};
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 podramos
acceder a la funcin 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 notacin simple . o con la notacin tipo bracket {}. La primera es
sumamente sencilla de utilizar: obj.x hace referencia a la propiedad
x del objeto obj. La notacin via bracket es mucho ms verstil,
puesto que cualquier expresin que se incluya entre los brackets
se evaluar y converir a un string utilizndose como nombre de
propiedad, veamos el mismo ejemplo obj['x']
Normalmente, utilizars la notacin por punto si conoces el nombre
de la propiedad previamente y en el caso de que lo tengas que
determinar dinmicamente utilizars la notacin 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 difcil testear?'
description: 'Conversacin 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 difcil testear?',
description: 'Conversacin 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-
mrica. Deja de hacer ms 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 operacin que los desarrolladores de
Python podrn reconocerlas fcilmente. Vamos a volver a realizar
un mapeo mediante comprensin 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 adems 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 funcin de filtrado. Ahora ya puedes ir a tu
consola de CoffeeScript para jugar con tus heroes y comprensiones.
Si todava no estas impresionado por esta capacidad que tiene este
lenguaje, solo me queda sorprendente con una implementacin del
mtico 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 podas prever cada instancia de Hero dice que tiene un poder
distinto porque el mtodo 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 creacin 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 captulos anteriores, todas las variables que se
declaran en una funcin solo existen en el mbito propio de la
funcin. Cada nuevo fichero que compila CoffeeScript a su vez
genera una funcin autoejecutable, con su propio mbito, y por
lo tanto si tenemos dos ficheros las variables de un fichero no
sern visibles desde el otro, a menos que estn 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 cmo podemos acceder a esas funciones
y variables que tenemos en mdulos 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. Modularizacin 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 mdulos 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. Modularizacin 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