Zahlreiche Sprachen versuchen das in die Jahre gekommene Javascript zu verbessern oder sogar abzulösen. Dazu gehört das recht frische Coffeescript. Es nimmt einige behutsame, aber effiziente Vereinfachungen vor, lässt sich in Javascript-Code übersetzen und ist damit erstaunlich erfolgreich.
An den Weihnachtsfeiertagen des Jahres 2009 hatte Jeremy Ashkenas alle Hände voll zu tun. Mit großem Eifer veröffentlichte er hintereinander mehrere Versionen einer neuen Programmiersprache. Sie sollte einfacher zu lesen sein als Javascript und sich doch in Javascript-Code übersetzen lassen. Bereits zwei Jahre später integrierte das Ruby-on-Rails-Projekt die zwischenzeitlich Coffeescript [1] getaufte Sprache. Im September 2012 verkündete schließlich die Firma Dropbox, sie habe den kompletten Javascript-Code ihres Browser-Clients nach Coffeescript portiert [2]. Die vom Cloudspeicherdienst vorgelegten Zahlen beeindrucken: Der Code des Dropbox-Clients schrumpfte um etwa 21 Prozent.
Sanft verbessert
Coffeescript ist allerdings keine komplett neu entwickelte Sprache. Jeremy Ashkenas hat sich einfach das gute alte Javascript genommen, darin ein paar komplexere Konstrukte gegen besser lesbare Abkürzungen ausgetauscht und das Ganze noch mit etwas Haskell und Ruby gewürzt. So darf der Programmierer beispielsweise anstelle von
alert("Hallo Welt!");
einfach kurz und knapp
alert "Hallo Welt!"
schreiben. Damit ist Coffeescript-Code einerseits wesentlich kompakter und somit leichter zu warten, andererseits können Programmierer bestehenden Javascript-Code einschließlich existierender Frameworks direkt in Coffeescript weiterverwenden. Wie im Fall von »alert« sind einige der kürzeren Schreibweisen optional, die meisten jedoch Pflicht.
Da Coffeescript-Code zurzeit nicht direkt in Browsern läuft, stellen die Spracherfinder einen Compiler bereit, der Coffeecript-Code in normales Javascript überführt. Der dabei erzeugte Code soll für Menschen gut lesbar sein, beim Test mit Javascript Lint [3] nicht durch Syntaxfehler oder Warnungen auffallen, in jeder Javascript-Laufzeitumgebung funktionieren und zumindest genau so schnell sein wie äquivalenter, per Hand geschriebener Javascript-Code.
Das alles versprechen zumindest Jeremy Ashkenas und seine mittlerweile zahlreichen Helfer. Als Bonus liegt dem Compiler noch ein kleiner Interpreter bei, der Coffeescript-Programme direkt auf der Kommandozeile ausführt. Wie man die Werkzeuge unter Linux zum Laufen bringt, verrät der Kasten “Installation”.
Installation
Der Coffeescript-Compiler ist selbst in Coffeescript geschrieben. Das hat den Vorteil, dass man ihn sogar in einem Browser ausführen kann. Der Kommandozeileninterpreter nutzt jedoch das Javascript-Framework Node.js [4]. Zur Installation sind daher unbedingt Node.js sowie der Node Package Manager (»npm« ) erforderlich. Einige Distributionen, etwa Ubuntu, halten beides vor. Sobald die Pakete eingespielt sind, genügt zur Installation der Coffeescript-Werkzeuge das Kommando:
sudo npm install -g coffee-script
Den Quellcode der Werkzeuge gibt es zusätzlich auf der Coffeescript-Homepage [1].
Um auf der Kommandozeile Coffeescript-Code auszuführen, legt ihn der Programmierer in einer Datei mit der Endung ».coffee« ab und lässt den Interpreter darauf los (Abbildung 1):

Abbildung 1: Zunächst führt der Interpreter das Skript »hallo.coffee« aus, dann übersetzt es der Compiler in Javascript-Code.
coffee hallo.coffee
An dieser Stelle gibt es allerdings zwei kleine Stolpersteine: Zunächst startet der Interpreter das Programm »node« , das unter einigen Distributionen jedoch »nodejs« heißt. Wer eine entsprechende Fehlermeldung erhält, muss entweder per Hand einen Symlink einrichten oder noch einmal einen Blick in den Paketmanager werfen: Ubuntu bietet für diese Zwecke eigens das Paket »nodejs-legacy« an.
Des Weiteren stehen auf der Kommandozeile nicht alle Javascript-Funktionen bereit. Das gilt insbesondere für solche Exemplare, die auf ein Fenster angewiesen sind, etwa »alert()« . Um dem Interpreter ein “Hallo Welt” zu entlocken, muss der Programmierer ihn daher mit dem Coffeescript-Code
conosole.log "Hallo Welt"
füttern. Den Compiler weckt er schließlich noch über den Parameter »-c« :
coffee -c hallo.coffee
Der aus »hallo.coffee« erzeugte Javascript-Code landet dabei in der Datei »hallo.js« .
Im Browser
Die ersten Gehversuche mit Coffeescript kann der Neugierige aber auch direkt im Web machen. Dazu klickt er auf der Coffeescript-Homepage auf »Try Coffeescript« und hinterlegt seinen Code im blauen Fenster, der so genannten Coffeescript-Konsole. Diese übersetzt schon während des Tippens im Hintergrund den Code und zeigt das Resultat auf der rechten Seite an. Ein Klick auf »Run« führt die Eigenkreation schließlich aus.
Wie das einfache Hallo-Welt-Beispiel weiter oben zeigt, darf der Coffeescript-Coder das Semikolon am Zeilenende weglassen, der Zeilenumbruch dient als Ersatz. Auch die geschweiften Klammern haben ausgedient: Wie in Python klären Einrückungen, zu welchem Block eine Zeile gehört. Wie übersichtlich man damit ein Objekt definiert, zeigt Listing 1, den daraus erzeugten Javascript-Code Listing 2. Variablen darf der Programmierer direkt benutzen, das passende »var« ergänzt Coffeescript beim Kompilieren. Wie in Shellskripten leitet das Hash-Zeichen »#« einen Kommentar ein.
Listing 2
Javascript-Code zu Listing 1
01 // Javascript
02 var autos;
03
04 autos = {
05 bmw: {
06 farbe: "Blau",
07 baujahr: 1990
08 },
09 vw: {
10 farbe: "Rot",
11 baujahr: 1988
12 }
13 };
Listing 1
Definition mit Einrückungen
01 # Coffeescript 02 autos = 03 bmw: 04 farbe: "Blau" 05 baujahr: 1990 06 vw: 07 farbe: "Rot" 08 baujahr: 1988
Zahlenfolgen darf man prägnant als Bereich schreiben. So steht etwa »[1..10]« für die Zahlen von 1 bis 10. Diese Notation lässt sich in Coffeescript dazu verwenden, elegant Teile aus einem Array auszuschneiden. Ein Beispiel dafür zeigt Listing 3. Zwei Punkte zwischen den Positionsangaben – also »[1..3]« – übernehmen auch die Position 1 und die Position 3 mit ins neue Array, bei drei Punkten – »[1…3]« – wandert das Element an Position 3 (in Listing 3 der »BMW« ) nicht mit in das neue Array.
Listing 3
Arrays manipulieren
01 autos = ["VW", "Mercedes", "Ferrari", "BMW"] 02 03 rennwagen = autos[1..3] 04 formel1 = autos[1...3] 05 06 alert rennwagen 07 alert formel1
Funktional
In Funktionsaufrufen darf man die Klammern weglassen. Den Ausdruck
console.log fakultaet x
übersetzt Coffeescript in »console.log(fakultaet(x))« . Für die Deklaration von Funktionen gibt es zudem eine vereinfachte Schreibweise. So fällt das Schlüsselwort »function« weg und der Rückgabewert steht hinter einem Pfeil »->« . Ebenso darf der Programmierer Argumente mit Standardwerten belegen. Coffeescript macht aus der Zeile
quadrieren = (x = 2) -> x * x
das Javascript aus Listing 4. Coffeescript-Funktionen dürfen beliebig viele Argumente übergeben bekommen. In Listing 5 muss man die Funktion »a« mindestens mit zwei Argumenten aufrufen, etwa »a 1,2« . Diese beiden Zahlen landen in den Variablen »x« und »y« . Alle weiteren übergebenen Werte wandern in das Array »rest« . Die drei Punkte (Splats) zeigen an, dass der Aufrufer hier beliebig viele Werte übergeben darf. Listing 5 liefert somit als Ausgabe »3,4,5,6« .
Listing 5
Splats
01 a = (x, y, rest...) -> 02 alert rest 03 04 a 1,2,3,4,5,6
Listing 4
Übersetzte Funktion quadrieren
01 var quadrieren;
02
03 quadrieren = function(x) {
04 if (x == null) {
05 x = 2;
06 }
07 return x * x;
08 };
Möchte der Programmierer die Inhalte von zwei Variablen »a« und »b« vertauschen, benötigt er normalerweise eine dritte als Zwischenspeicher. Coffeescript kennt jedoch eine kürzere Notation:
[a, b] = [b, a]
Sie weist die Variableninhalte auf der rechten Seite den Variablen auf der linken zu. Auf der rechten Seite darf sogar ein beliebiges Array stehen. Dieses Konzept namens Destructuring Assignment soll übrigens auch in einer der nächsten Versionen des Standards Ecmascript Einzug halten [5].
Mit dieser Notation lassen sich außerdem auch Funktionen schreiben, die mehrere Werte zurückgeben. Listing 6 zeigt den Trick: Dort gibt die Funktion »adresse« einfach ein Array mit den entsprechenden Werten zurück, die Coffeescript in der zweiten Zeile den Variablen »name« , »strasse« und »ort« zuweist. Wem jetzt der Kopf schwirrt, der sollte einen Blick auf den resultierenden Javascript-Code in Abbildung 2 werfen.
Listing 6
Destructuring Assignment
01 adresse = () -> ["hans", "musterstr", "musterdorf"] 02 [name, strasse, ort] = adresse() 03 alert name

Abbildung 2: Die Coffeescript-Konsole im Browser: Die Elemente des von »adresse()« zurückgelieferten Array landen jeweils in den Variablen »name«, »straße« und »ort«.
Packstation
Das Schlüsselwort »do« führt eine Funktion sofort aus. Ein einfaches Beispiel gibt Listing 7, die Javascript-Übersetzung zeigt Listing 8: Die Variable »quadrat« enthält dort nicht etwa eine Funktion, die »x * x« ausrechnet, sondern das Ergebnis »9« . Damit das funktioniert, stülpt »do« der Funktion »x * x« zunächst eine leere Funktion über, leitet dann alle Argumente weiter und startet das entstandene Konstrukt. Auf diese Weise lassen sich übrigens auch Closures basteln. Apropos Verpacken: Um bei der Zusammenarbeit mit vorhandenem Javascript-Code nicht in Probleme zu laufen, verpackt Coffeescript den kompletten Code noch einmal in eine anonyme Funktion »(function(){ … })();« . Das verhindert zwar, dass Variablen versehentlich global werden, wer jedoch Variablen außerhalb von Coffeescript anbietet, muss sie beispielsweise als Eigenschaften dem »window« -Objekt anhängen.
Listing 8
Javascript-Code zu Listing 7
01 var quadrat, x;
02 x = 3;
03
04 quadrat = (function(x) {
05 return x * x;
06 })(x);
07
08 alert(quadrat);
Listing 7
Einsatz von do
01 var quadrat, x;
02 x = 3;
03
04 quadrat = (function(x) {
05 return x * x;
06 })(x);
07
08 alert(quadrat);
Alles unter Kontrolle
Auch bei »if« -Abfragen fallen die geschweiften Klammern weg, den Rumpf trennt in Zweifelsfällen ein »then« :
if [...] then [...] else [...]
Zusätzlich zu den aus Javascript bekannten logischen Operatoren wie »&&« oder »||« bietet Coffeescript die besser lesbaren »and« , »or« , »is« und »not« an. Den Vergleichsoperator »==« ersetzt der Coffescript-Compiler immer in »===« sowie »!=« in »!==« . Außerdem darf der Programmierer bei Einzeilern eine schlanke Postfix-Notation verwenden. Listing 9 zeigt das Javascript zu der folgenden Zeile:
Listing 9
Übersetzte if-Abfrage
01 var ps;
02
03 if (bmw || mercedes) {
04 ps = 230;
05 }
ps = 230 if bmw or mercedes
Vergleichsoperatoren kann der Entwickler wie in Python hintereinanderhängen und damit rasch testen, ob etwa die Temperatur im erträglichen Bereich liegt:
if (15 > temperatur < 30) then [...]
Wie findet der Programmierer heraus, ob eine Variable »x« existiert? Coffeescript bietet dafür den praktischen Fragezeichen-Operator an. Der liefert »true« , wenn die Variable schon existiert und zudem nicht »null« ist:
if x? then x = 1
Dieser Operator hilft auch dabei, in hintereinandergeschalteten Aufrufen »null« -Referenzen “aufzusaugen”:
autonr = garage.oeffnen?().auto?.kennzeichen()
Sollte ein Objekt (wie das »auto« ) oder eine Funktion (wie »garage.oeffnen()« ) nicht existieren, liefert der ganze Ausdruck »undefined« zurück, es entsteht also kein Type-Error.
Fallunterscheidungen lassen sich in Javascript durch das kompakte »switch« ersetzen. In Coffeescript hat sich dieses der Klammern, der Breaks und des Default entledigt, Listing 10 zeigt ein Beispiel. Coffeescript ersetzt die »for« -Schleifen aus Javascript durch ein »for […] in« . Die Zeile
Listing 10
Fallunterscheidung mit switch
01 sprache = "DE" 02 switch sprache 03 when "EN" then alert "Hello!" 04 when "DE", "AT" then alert "Tag!" 05 else alert "Sprache unbekannt"
alert auto for auto in ["bmw", "vw", "opel"]
verwandelt der Compiler in Listing 11. Wie dort zu sehen ist, durchläuft das Programm das Array mit den Autonamen. Dabei steckt es den aktuellen Namen in die Variable »auto« , die es wiederum an die Funktion »alert« übergibt.
Listing 11
Übersetzte for-Schleife
01 var auto, _i, _len, _ref;
02
03 _ref = ['bmw', 'vw', 'opel'];
04 for (_i = 0, _len = _ref.length; _i < _len; _i++) {
05 auto = _ref[_i];
06 alert(auto);
07 }
For-Comprehensions
Ergänzend erlaubt es »for […] in« , so genannte Comprehensions aufzubauen, also Funktionen, die aus einer Liste eine neue generieren. Das folgende Beispiel durchläuft die Zahlen 1 bis 10 in durch die Angabe »by 2« erzwungenen Zweierschritten. Jede der Zahlen 1, 3, 5, 7, 9 quadriert Coffeescript (»x*x« ) und speichert schließlich alle Ergebnisse im Array »quadrate« :
quadrate = (x*x for x in [1..10] by 2)
Das per »alert quadrate« ausgegebene Ergebnis lautet damit »1,9,25,49,81« . Alternativ kann man auch die Eigenschaften von Objekten durchlaufen. Die Zeilen aus Listing 12 führen zur Ausgabe »BMW ist Blau, VW ist Rot« . Das Listing zeigt auch noch eine weitere, von Ruby abgeschaute Notation: »#{farbe}« ersetzt Coffeescript innerhalb des Strings durch den Inhalt der Variablen »farbe« , analog gilt dies für die »marke« . Auf diese Weise lassen sich elegant komplexere Zeichenketten zusammensetzen.
Listing 12
Iteration über Eigenschaften
01 autos = BMW: "Blau", VW: "Rot"
02
03 daten = for marke, farbe of autos
04 " #{marke} ist #{farbe}"
05
06 alert daten
Während die alte »for« -Schleife aus Javascript zumindest in der aktuellen Version von Coffeescript nicht existiert, kennt die Sprache sehr wohl noch die »while« -Schleife. Besteht sie nur aus einer Zeile, lässt sie sich in Postfix-Notation verwenden. In folgendem Beispiel würde »while« so lange »gasgeben()« aufrufen, wie »geschwindigkeit« kleiner als »maximum« ist:
gasgeben() while geschwindigkeit < maximum
In Coffeescript darf man die »while« -Schleife aber auch als Ausdruck verwenden, der ein Array mit den Ergebnissen aus jedem Schleifendurchlauf zurückgibt. Listing 13 zeigt ein Beispiel dafür: Die »while« -Schleife durchläuft einfach die Zahlen von 9 bis 1 und steckt sie in einen String. Alle so erzeugten Strings landen zum Schluss wiederum im Array »countdown« . Bequeme Programmierer dürfen schließlich noch »until« anstelle von »while not« sowie »loop« anstelle von »while true« schreiben.
Listing 13
Erweiterte while-Schleife
01 zaehler = 10
02 countdown = while zaehler -= 1
03 "#{zaehler} Bier sind noch über."
04 alert countdown
Rückgabewerte
Listing 13 liefert einen Wert zurück, obwohl nirgends ein »return« steht. Dies ist möglich, da Coffeescript alle Anweisungen als Ausdruck (Expression) ansieht. Funktionen geben immer automatisch ihren letzten Wert zurück – vorausgesetzt der Coffeescript-Programmierer setzt nicht selbst »return« ein. Darüber hinaus kann er Variablenzuweisungen innerhalb eines Ausdrucks vornehmen, was zu lustigen Zeilen wie der folgenden führt:
sieben = (fuenf = 5) + (zwei = 2)
Einige Anweisungen in Javascript wie »break« oder »continue« lassen sich nicht in einen Ausdruck überführen. Nutzt man diese in Coffeescript, übernimmt sie der Compiler einfach ins fertige Javascript. Wer solche Anweisungen einsetzt, muss folglich wissen, was er tut.
Dank des Schlüsselworts »class« lassen sich auch Klassen in Coffeescript kompakter und vor allem lesbarer aufschreiben. Listing 14 zeigt dafür ein Beispiel. Die Klasse »Punkt« besitzt dort einen Konstruktor, der zwei Argumente übergeben bekommt. Das »@« sorgt im Konstruktor dafür, dass Coffeescript die übergebenen Werte direkt den Attributen namens »x« und »y« zuweist. Der Rumpf des Konstruktors kann somit leer bleiben (nach dem Pfeil folgt eine Leerzeile).
Listing 14
Klassen und Vererbung
01 class Punkt 02 constructor: (@x, @y) -> 03 04 zeichne: -> 05 alert "Position: " + @x + ", " + @y 06 07 class Rechteck extends Punkt 08 constructor: (a, b, @breite) -> 09 super a,b 10 11 zeichne: -> 12 alert "Breite: " + @breite 13 super 14 15 form = new Rechteck 1,2,3 16 form.zeichne()
Allgemein ist die Notation »@x« eine Kurzschreibweise für »this.x« . Davon macht auch die Methode »zeichne« Gebrauch. Die Klasse »Rechteck« ist von »Punkt« abgeleitet, »super« ruft jeweils die entsprechende Methode in der Oberklasse auf – »super« in der Methode »zeichne« ruft folglich die Methode »zeichne« der Klasse »Punkt« auf. Zum Schluss erzeugt Listing 14 noch ein neues »Rechteck« und ruft dessen Methode »zeichne« auf. Konstruktoren tragen den Namen ihrer Klasse, in Listing 14 ist »form.constructor.name« daher »Rechteck« .
Schlaue Callbacks
In Javascript-Code bezeichnet »this« immer das Objekt, an das die gerade ablaufende Funktion gebunden ist. Das führt zu Problemen, wenn Event-Handler und Callback-Funktionen zum Einsatz kommen: Wird die Callback-Funktion aufgerufen, verweist »this« auf genau jenes DOM-Element, welches das Ereignis erzeugt hat. Der Programmierer müsste folglich auf den Einsatz von »this« innerhalb der Callback-Funktion verzichten – oder er nutzt in Coffeescript den Doppelpfeil. Dann zeigt »this« auf das Objekt, in dem die Callback-Funktion definiert ist. Ein Beispiel dafür liefert Listing 15.
Listing 15
Doppelpfeil-Operator
01 Einkauf = (kunde, produkt) ->
02 @kunde = kunde
03 @produkt = produkt
04
05 $('.shopping_cart').bind 'click', (event) =>
06 @kunde.kauft @produkt
Kuchen backen
Zusammen mit Compiler und Interpreter wandert bei der Coffeescript-Installation auch das rudimentäre Buildtool »cake« auf die Festplatte. Analog zu Make wertet es eine Steuerdatei namens »Cakefile« aus, die eine oder mehrere Aufgaben (Tasks) vorgibt. Das »Cakefile« selbst ist wieder ein Coffeescript, der Programmierer darf somit auch entsprechenden Code darin unterbringen.
Listing 16 liefert ein Beispiel für eine solche Datei. Es holt zunächst ein Hilfsobjekt hinzu und definiert dann eine Funktion »build« , die den »coffee« -Compiler alle Coffeescript-Dateien im Unterverzeichnis »src« übersetzen lässt. Abschließend definiert es noch eine Task namens »build« , die einfach nur die Funktion »build()« aufruft.
Listing 16
Cakefile
01 cp = require 'child_process' 02 03 build = (callback) -> 04 coffee = cp.spawn 'coffee', ['-c', 'src'] 05 06 task 'build', 'Kompiliere unter src/', -> 07 build()
Um diese Task anzustoßen, ruft der Entwickler »cake build« im Verzeichnis mit dem Cakefile auf, »cake« ohne Parameter zeigt nur die vorhandenen Tasks und deren Beschreibung an. Letztere lautet in Listing 16 »Kompiliere unter src/« .
Fazit
Die Macher versprechen nicht zu viel: Coffeescript-Code ist gegenüber Javascript in weiten Teilen leichter lesbar und erstaunlich kompakt – manchmal allerdings auch zu kompakt. So brauchen viele Programmierer etwas Übung, bis sie auf einen Blick erkennen, wer in der Anweisung »rechne x, (i) -> quadrat i« wen aufruft. Glücklicherweise hilft hier die vorzügliche Coffeescript-Konsole im Web, die den Code schon direkt beim Tippen in Javascript übersetzt.
Der Umstieg von Dropbox auf Coffeescript beweist, dass die neue Sprache alltagstauglich ist. Eine Liste mit weiteren Coffeescript-Anwendungen liefert das Wiki des Projekts [6]. Ausführlichere Informationen zur Sprache und Syntax enthält das kostenlose Buch “Smooth Coffeescript” [7]. Das Werk “The Little Book on Coffeescript” darf man nur in einer älteren, aber immer noch weitestgehend gültigen Auflage im Internet kostenfrei lesen [8]. Die offiziell verfügbare Anleitung auf der Coffeescript-Homepage ist kurz und knackig, richtet sich aber an Javascript-Programmierer – und genau die sollten aufgrund der vielen Vorteile unbedingt reinschnuppern.
Infos
- Coffeescript: http://coffeescript.org
- Dropbox-Tech-Blog, “Dropbox dives into Coffeescript”: https://tech.dropbox.com/2012/09/dropbox-dives-into-coffeescript/
- Javascript Lint: http://www.javascriptlint.com
- Node.js: http://nodejs.org
- “Destructuring Assignment in ECMAScript”: http://wiki.ecmascript.org/doku.php?id=harmony:destructuring
- Projekte und Anwendungen, die Coffeescript nutzen: https://github.com/jashkenas/coffee-script/wiki/In-The-Wild
- E. Hoigaard, “Smooth Coffeescript”: http://autotelicum.github.com/Smooth-CoffeeScript/
- Alex MacCaw, “The Little Book on Coffeescript”: http://arcturo.github.com/library/coffeescript/






