Introduction à coffeescript

Publié le 2 août 2011 par Martin Catty | front

Cet article est publié sous licence CC BY-NC-SA

C’est quoi coffeescript ?

Coffeescript est un langage compilé en javascript. Il n’est pas interprété nativement, il doit d’abord être converti en javascript afin d’être utilisable.

Il est à noter que coffeescript sera inclus directement dans rails 3.1.

Pourquoi l’utiliser ?

Quelques arguments avant de rentrer dans le vif du sujet:

  • Javascript généré lisible.
  • Syntaxe proche de ruby, très compréhensible.
  • Mécanisme de classe et d’héritage simplifié.
  • Inclus dans rails 3.1.
  • Volume de code à maintenir moindre.
  • Utilisation de concepts avancés simplifiés (classe, binding…).

Syntaxe

En coffee, vous n’avez pas besoin d’utiliser explicitement le mot clé var, ni les «;», ceux ci seront générés automatiquement pour vous.

Les idées reprises de ruby

  • Les parenthèses sont facultatives du moment qu’elles ne posent pas de problèmes à l’interprétation.
  • Le dernier résultat interprété est retourné, pas besoin de return explicite.
  • Il est possible de faire de l’interpolation sur les strings du type: “foo #{bar}” ou bar est une variable.
  • Le if peut être placé en fin d’expression.
  • Le mot clé unless existe.
  • Les commentaires sont définis avec #.

L’égalité stricte avec le mot clé is

1 is 1
# true

1 is "1"
# false

Le or= et ?=

or= est l’équivalent de   =

Attention avec a or= b

a prendra la valeur de b si a est null, undefined, false, vaut 0 ou “” qui valent tous false une fois convertis en booléen.

a ?= b affectera la valeur de b dans a uniquement si a est null ou undefined.

Il s’agit en fait d’un raccourci syntaxique. Chaque valeur définie peut être testée avec ?

a = null
a?
=> false
b = 42
b?
=> true

Equivalences de notation:

# équivalences
a ?= b
a = b unless a?
a = a ? b

Les fonctions

A chaque fois que vous voyez -> il s’agit en fait d’une définition de fonction. C’est un concept central dans coffeescript.

Définissions notre première fonction anonyme:

() -> console.log "Hello from coffee"

donnera en js:

(function() {
  return console.log("Hello from coffee");
});

Si vous avez besoin de pouvoir rappeler votre fonction, ce qui sera souvent le cas, excepté pour les callbacks, il vous suffit de l’affecter:

cube = (num) -> Math.pow num, 3

donnera en js:

cube = function(num) {
  return Math.pow(num, 3);
};

Je ne vous montre qu’une partie du code généré mais le compilateur généré aussi les déclarations de variable pour vous et enrobe le tout dans une fonction anonyme (voir portée).

Nous avons vu comment passer un argument et nommer une fonction.

Important: la génération des fonctions se fait de la forme suivante:

var f = function() {}

// et non:

function f() {}

Les deux syntaxes sont complètement valides, mais c’est la première qui a été retenue pour des raisons de compatibilité avec IE.

Toutefois celle ci est plus contraignante car vous ne pouvez appeler une fonction que quand elle a été définie.

L’ordre de définition est donc très important.

Paramètres par défaut

Les fonctions offrent la prise en charge des arguments par défaut.

helloWorld = (street = "Lille") -> console.log "Hello world from #{street}."

helloWorld()
# Hello world from Lille.
helloWorld("Paris")
# Hello world from Paris.

Splat

Les splat permettent de définir un nombre variable d’arguments qui seront récupérés comme un tableau:

f = (a, b..., c) ->

f("a", "foo", "bar", "baz", 42)

# b => [ 'foo', 'bar', 'baz' ]

A noter que les arguments hors splat sont les premiers à être assignés:

f("a", 42)

b # []

Les splats peuvent être utilisés hors fonction:

[a..., b] = ["foo", "bar", 42]
a # [ 'foo', 'bar' ]

Portée

Le moindre code coffeescript est transformé en un code javascript enrobé dans une fonction anonyme.

Cela présente un avantage et un inconvénient. D’une part vous ne risquez pas de rencontrer des conflits de nom ou de portée car le code générée ne peut pas écraser un code existant (namespacing).

D’autre part cela peut poser des problèmes si deux fichiers ont besoin d’interagir.

Au niveau compilation il est possible d’éviter cela avec l’option –no-wrap.

Au niveau code il est possible d’utiliser l’objet global window pour affecter les différents modules.

Par exemple pour déclarer une classe utilisable depuis un autre morceau de code coffee:

window.App = class App

This

Il est facile de s’arracher les cheveux si on maitrise mal la portée en javascript. Posez vous toujours la question: qui est this dans ce contexte ?

Coffeescript vous offre un raccourci syntaxe pour accèder directement à la propriété d’un objet:

@attribute équivaut à this.attribute

C’est pratique, notamment pour les classes (voir partie Les classes), mais aussi pour tout objet.

person  = { firstname: "Martin", lastname: "Catty" }
person.introduce = -> console.log "Bonjour, je suis #{@firstname} #{@lastname}."
person.introduce()
# Bonjour, je suis Martin Catty.

Cette syntaxe vient avec un raccourci syntaxique. Par exemple dans le cas classique de définition d’un setter.

setAttribute = (attribute) -> @attribute = attribute

peut être raccourci en:

setAttribute = (@attribute) ->

Coffeescript offre un mécanisme extrèmement simple pour garantir le this actuel. Plutôt que de définir votre fonction avec -> il suffit d’utiliser =>

Exemple:

registerEvents = (node) ->
  node.bind "click", (event) =>

Les classes

Coffeescript introduit un mécanisme de classe très puissant:

window.Animal = class Animal

window.Dog = class Dog extends Animal
  # convention pour une constante mais utilise var
  STATES = ["sleeping", "eating"]

  constructor: ->
    # appelle le constructeur d'Animal
    super

  # méthode de l'objet
  speak: ->
    console.log "woff."

  # variable de classe
  @count: 0

  # méthode statique
  @incCount: -> @count++

Utiliser coffeescript avec rails (avant 3.1)

La convention est de stocker ses fichiers dans app/coffeescripts/

Ensuite il s’agit de générer les fichiers correspondant. Pour cela guard fait très bien l’affaire.

# Gemfile:

group :development do
  gem 'guard'
  gem 'guard-coffeescript'
end
# Guardfile

group 'frontend' do
  guard 'coffeescript', :output => 'public/javascripts/compiled' do
    watch(%r{^app/coffeescripts/.+\.coffee$})
  end
end
bundle exec guard

Complément

Il est possible d’appeler du js depuis du coffee avec les backticks `.

Conclusion

L’apprentissage de coffeescript est assez simple, surtout si vous utilisez actuellement ruby et que vous avez une bonne base en javascript.

Le temps d’apprentissage me semble largement valoir le gain en temps de développement et maintenance.

Bref, l’essayer c’est l’adopter.

L’équipe Synbioz.

Libres d’être ensemble.