読者です 読者をやめる 読者になる 読者になる

CoffeeScript言語リファレンスを日本語訳しました

tech

最近CoffeeScriptというものを知って少し調べてみたら、本家の言語リファレンスがそんなに多くなかったので日本語訳に挑戦してみました。
http://coffeescript.org/

CoffeeScriptとはJavaScriptにコンパイル可能なシンプルな言語です。クラスベースのオブジェクト指向、Python/Rubyを参考にした内包表記、文字列内変数展開、スタティックスコープなどなどがあります。


先にネタバラシをしておくと日本語訳は既に別の方がしていました\(^o^)/
http://sites.google.com/site/sappariwiki/coffeescript


しかも僕の日本語訳より全然わかりやすい!ぶっちゃけ英語は大の苦手なので、翻訳したはいいけど間違ってるところは色々あるはずです。
でもまあ技術的なドキュメントの翻訳に挑戦したのは、数年前に前職でRDP(Rmote Desktop Protocol)を少しやった程度なので、勉強がてらやってみてよかったです。
(http://www.rdesktop.org/みたら1.7.1リリースされてる!開発止まってるかと思ったけどすごい!)


では以下日本語訳です。CoffeeScriptのバージョンは1.2.0です。
間違いやわかりにくいところが多々あると思いますが、何かの参考になれば幸いです!
原著 : http://coffeescript.org/ Language Reference
(ちなみにこの分量で10時間くらいかかりましたw)



言語リファレンス

このリファレンスは上から下へ読めるように構成されています。後ろのセクションでは前のセクションで紹介した考えや構文を使用しています。JavaScriptを知っていることが前提です。以下の全てのソースではCoffeeScriptを左に、Javascriptにコンパイルされたものを右に表記します。


多くのサンプルコードは右側のrunボタンを押すことで実行でき、loadボタンを押すことで"Try CoffeeScript"コンソールに読み込むことができます。


まず初めに、CoffeeScriptはコードブロックを区切るのにホワイトスペースを使用します。セミコロンを式の終わりに使用する必要はありません。行の終わりも同様です(複数の式を一行にするために使用することはできますが)。関数、if、switch、try/catchを{}で囲む代わりに、インデントを使用します。


関数に引数を渡す場合に()を使用する必要はありません。暗黙的な呼び出しは行もしくはブロック式の先頭から終わりにかけラップします。

console.log sys.inspect object → console.log(sys.inspect(object));

関数

関数は省略可能な()内の引数のリスト、矢印、関数本文によって定義されます。空の関数は"->"のように表されます。

square = (x) -> x * x
cube   = (x) -> square(x) * x
var cube, square;

square = function(x) {
  return x * x;
};

cube = function(x) {
  return square(x) * x;
};


関数は引数のデフォルト値を持つことも出来ます。非nullなひきすうによってデフォルト値は上書きされます。

fill = (container, liquid = "coffee") ->
  "Filling the #{container} with #{liquid}..."
var fill;

fill = function(container, liquid) {
  if (liquid == null) liquid = "coffee";
  return "Filling the " + container + " with " + liquid + "...";
};

オブジェクトと配列

CoffeeScriptのオブジェクトと配列のリテラルはJavaScriptととても似ています。各プロパティがそれだけの行にリストされている場合はカンマは省略可能です。オブジェクトは明示的な{}の代わりにYAMLに似たインデントを使って作ることもできます。

song = ["do", "re", "mi", "fa", "so"]

singers = {Jagger: "Rock", Elvis: "Roll"}

bitlist = [
  1, 0, 1
  0, 0, 1
  1, 1, 0
]

kids =
  brother:
    name: "Max"
    age:  11
  sister:
    name: "Ida"
    age:  9
var bitlist, kids, singers, song;

song = ["do", "re", "mi", "fa", "so"];

singers = {
  Jagger: "Rock",
  Elvis: "Roll"
};

bitlist = [1, 0, 1, 0, 0, 1, 1, 1, 0];

kids = {
  brother: {
    name: "Max",
    age: 11
  },
  sister: {
    name: "Ida",
    age: 9
  }
};

JavaScript中では"class"のような予約後はオブジェクトのプロパティとして引用符で囲まずに使うことができません。CoffeeScriptはオブジェクトのキーとして予約後が使われていることを判断し、引用符で囲みます。つまりそれについて心配する必要はありません(jQueryを使う場合など)

$('.account').attr class: 'active'

log object.class
$('.account').attr({
  "class": 'active'
});

log(object["class"]);

レキシカルスコープと安全な変数

CofeeScriptコンパイラは変数全てがレキシカルスコープ内で適切に宣言されているか確認します。varを自身で書く必要はありません。

outer = 1
changeNumbers = ->
  inner = -1
  outer = 10
inner = changeNumbers()
var changeNumbers, inner, outer;

outer = 1;

changeNumbers = function() {
  var inner;
  inner = -1;
  return outer = 10;
};

inner = changeNumbers();

全ての変数宣言がそれらが初めて現れたスコープの上方に押し上げられていることに注目してください。outer変数は内部関数のなかで最宣言されていません、なぜならそれは既にスコープにあるからです。一方で関数内のinner変数は同じ名前の外部変数の値を変更すべきではありません、なのでinner自身の宣言があります。

この動作はRubyのローカル変数のためのスコープと事実上同じです。varキーワードを直接使うことができないため、outer変数をローカル変数とすることが不可能となり、outer変数を参照するしかありません。もしあなたが深くネストされた関数を書いている場合、外部変数の名前を誤って再利用してしまわないように注意してください。

Although suppressed within this documentation for clarity, CoffeeScriptの全出力は無名関数でラップされています。この安全なラッパーはvarキーワードの自動生成と合わさることで、事故によるグローバル名前空間の汚染を非常に難しい物にします。

もし他のスクリプトのためにトップレベルの変数を作りたい場合、window変数もしくはCommonJSのexports変数に追加してください。存在演算子(書き参照)はそれらの変数を追加する場所を把握するための信頼できる方法を提供します。もしあなたがCommonJSとブラウザの両方を対象としている場合は"export ? this"と書くことができます。

If, Else, Unless, 条件代入

if/elseは()と{}なしに書くことができます。関数や他のブロック式と同様に、複数行の条件はインデントで区切られます。ifもしくはunlessを行末に書く便利な後置形式もあります。

CoffeeScriptはif文を可能な場合は三項演算子を使用し、そうでない場合はクロージャを使ってJavaScriptにコンパイルします。明示的な三項演算子はCoffeeScriptにはありません。if文を一行で使用してください。

mood = greatlyImproved if singing

if happy and knowsIt
  clapsHands()
  chaChaCha()
else
  showIt()

date = if friday then sue else jill

options or= defaults
var date, mood;

if (singing) mood = greatlyImproved;

if (happy && knowsIt) {
  clapsHands();
  chaChaCha();
} else {
  showIt();
}

date = friday ? sue : jill;

options || (options = defaults);
load

Splats...

JavaScriptのオブジェクトを引数に使うことは可変引数を許可する関数で役に立ちます。CoffeeScriptは簡単に可変引数を生成する"..."を関数の定義および実行の両方に提供します。

gold = silver = rest = "unknown"

awardMedals = (first, second, others...) ->
  gold   = first
  silver = second
  rest   = others

contenders = [
  "Michael Phelps"
  "Liu Xiang"
  "Yao Ming"
  "Allyson Felix"
  "Shawn Johnson"
  "Roman Sebrle"
  "Guo Jingjing"
  "Tyson Gay"
  "Asafa Powell"
  "Usain Bolt"
]

awardMedals contenders...

alert "Gold: " + gold
alert "Silver: " + silver
alert "The Field: " + rest
var awardMedals, contenders, gold, rest, silver,
  __slice = Array.prototype.slice;

gold = silver = rest = "unknown";

awardMedals = function() {
  var first, others, second;
  first = arguments[0], second = arguments[1], others = 3 <= arguments.length ? __slice.call(arguments, 2) : [];
  gold = first;
  silver = second;
  return rest = others;
};

contenders = ["Michael Phelps", "Liu Xiang", "Yao Ming", "Allyson Felix", "Shawn Johnson", "Roman Sebrle", "Guo Jingjing", "Tyson Gay", "Asafa Powell", "Usain Bolt"];

awardMedals.apply(null, contenders);

alert("Gold: " + gold);

alert("Silver: " + silver);

alert("The Field: " + rest);

繰り返しと内包表記

CoffeeScriptで書くループのほとんどは配列、オブジェクト、レンジの内包表記でしょう。内包表記は省略可能な条件文と現在の配列インデックスを使ってforループに置き換えることができます。ループとは異なり、配列の内包表記は式であり、値を返し、値を必要とするところで使用することができます。

# Eat lunch.
eat food for food in ['toast', 'cheese', 'wine']

# Fine five course dining.
courses = ['greens', 'caviar', 'truffles', 'roast', 'cake']
menu i + 1, dish for dish, i in courses

# Health conscious meal.
foods = ['broccoli', 'spinach', 'chocolate']
eat food for food in foods when food isnt 'chocolate'
var courses, dish, food, foods, i, _i, _j, _len, _len2, _len3, _ref;
_ref = ['toast', 'cheese', 'wine'];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  food = _ref[_i];
  eat(food);
}

courses = ['greens', 'caviar', 'truffles', 'roast', 'cake'];

for (i = 0, _len2 = courses.length; i < _len2; i++) {
  dish = courses[i];
  menu(i + 1, dish);
}

foods = ['broccoli', 'spinach', 'chocolate'];

for (_j = 0, _len3 = foods.length; _j < _len3; _j++) {
  food = foods[_j];
  if (food !== 'chocolate') eat(food);
}

内包表現はeash/forEash, map, select/filterなどのループを使う必要が会った場所の殆どを置き換えることが可能です。例えば
"shortNames = (name for name in list when name.length < 5)"
もしループの始めと終わり、もしくは固定サイズの増減でステップ実行したい場合、内包表記に始めと終わりを指定したレンジを使うことができます。

countdown = (num for num in [10..1])
var countdown, num;

countdown = (function() {
  var _results;
  _results = [];
  for (num = 10; num >= 1; num--) {
    _results.push(num);
  }
  return _results;
})();

内包表記の値を変数に代入している上記の例ため、CoffeeScriptはイテレーションの結果を配列に集めていることに注目してください。それらの副作用だけのために故意に実行されるループで終わる関数があります。この場合、trueやnullのような意味のある戻り値を関数の最後に追加することで、内包表記の結果を意図せず返さないことに注意してください。

ステップの幅が1ではない範囲内包表記によるステップ実行を行うためにはbyキーワードを使ってください。例えば、
"evens = (x for x in [0..10] by 2)"

内包表記はオブジェクトのキーと値を使ってイテレートすることができます。配列中の値の代わりにオブジェクトのプロパティを取得するには内包表記のシグナルとしてofキーワードを使用します。

yearsOld = max: 10, ida: 9, tim: 11

ages = for child, age of yearsOld
  "#{child} is #{age}"
var age, ages, child, yearsOld;

yearsOld = {
  max: 10,
  ida: 9,
  tim: 11
};

ages = (function() {
  var _results;
  _results = [];
  for (child in yearsOld) {
    age = yearsOld[child];
    _results.push("" + child + " is " + age);
  }
  return _results;
})();

もしオブジェクト自身にのみ定義されているキーだけをイテレートしたい場合、ownキーワードを使って
"for own key, value of object"とします。これは"hasOwnProperty"チェックを追加することによってプロトタイプから継承されているプロパティを避けます。

CofeeScriptが提供している唯一の低レベルループはwhileループです。JavaScriptとの主な違いはwhileループは式としても使うことができ、各イテレーションの結果を含んだ配列を返します。

# Econ 101
if this.studyingEconomics
  buy()  while supply > demand
  sell() until supply > demand

# Nursery Rhyme
num = 6
lyrics = while num -= 1
  "#{num} little monkeys, jumping on the bed.
    One fell out and bumped his head."
var lyrics, num;

if (this.studyingEconomics) {
  while (supply > demand) {
    buy();
  }
  while (!(supply > demand)) {
    sell();
  }
}

num = 6;

lyrics = (function() {
  var _results;
  _results = [];
  while (num -= 1) {
    _results.push("" + num + " little monkeys, jumping on the bed.    One fell out and bumped his head.");
  }
  return _results;
})();

可読性のために、untilキーワードは"while not"と等価であり、loopキーワードは"while true"と等価です。


関数を生成するためにJavaScriptのループを使用するとき、ループ変数を確実にクロージングするためと生成された関数が最終的な値を共有しないためにクロージャでラップするのが普通です。CoffeeScriptは即座に関数を実行し、引数を渡すためにdoキーワードを提供します。

for filename in list
  do (filename) ->
    fs.readFile filename, (err, contents) ->
      compile filename, contents.toString()
var filename, _fn, _i, _len;

_fn = function(filename) {
  return fs.readFile(filename, function(err, contents) {
    return compile(filename, contents.toString());
  });
};
for (_i = 0, _len = list.length; _i < _len; _i++) {
  filename = list[_i];
  _fn(filename);
}

配列のスライスとレンジによる接合

レンジは配列のスライスを展開することができる。2つのドット(3..6)はレンジを含み(3,4,5,6)、3つのドット(3...6)はレンジの終わりを含まない(3,4,5)。

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

copy    = numbers[0...numbers.length]

middle  = copy[3..6]
var copy, middle, numbers;

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

copy = numbers.slice(0, numbers.length);

middle = copy.slice(3, 7);

同様の構文により配列の一部を新しい値を使って置き換えることができる。

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

numbers[3..6] = [-3, -4, -5, -6]
var numbers, _ref;

numbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

[].splice.apply(numbers, [3, 4].concat(_ref = [-3, -4, -5, -6])), _ref;

JavaScriptの文字列は変更不可能であり、接合できない。

全ては式(可能な限り)

CoffeeScriptの関数にはreturn文を追加していないのに、最後の値を戻り値となっていることに気づいているでしょう。CoffeeScriptコンパイラは全ての文が式として使えるようにします。returnキーワードは関数内の実行される可能性がある分岐に配置されます。

grade = (student) ->
  if student.excellentWork
    "A+"
  else if student.okayStuff
    if student.triedHard then "B" else "B-"
  else
    "C"

eldest = if 24 > 21 then "Liz" else "Ike"
var eldest, grade;

grade = function(student) {
  if (student.excellentWork) {
    return "A+";
  } else if (student.okayStuff) {
    if (student.triedHard) {
      return "B";
    } else {
      return "B-";
    }
  } else {
    return "C";
  }
};

eldest = 24 > 21 ? "Liz" : "Ike";

関数はかならず最後の値を戻り値とするが、明示的な復帰(return value)を書くことで関数からの早い復帰をすることが可能であるなら推奨します。

変数がそれまで現れていなかったとしても、代入は式の中で仕様するおとができる。なぜなら変数の宣言はスコープのトップで行われるからです。

six = (one = 1) + (two = 2) + (three = 3)
var one, six, three, two;

six = (one = 1) + (two = 2) + (three = 3);

JavaScriptで文として考えられるものがCoffeeScriptでは式の一部として使用されているとき、クロージャでそれらをラップすることで式へと変換されます。

# The first ten global properties.

globals = (name for name of window)[0...10]
var globals, name;

globals = ((function() {
  var _results;
  _results = [];
  for (name in window) {
    _results.push(name);
  }
  return _results;
})()).slice(0, 10);

try/catchステートメントを直接関数の引数として渡すのも同様である。

alert(
  try
    nonexistent / undefined
  catch error
    "And the error is ... #{error}"
)
alert((function() {
  try {
    return nonexistent / void 0;
  } catch (error) {
    return "And the error is ... " + error;
  }
})());
loadrun

JavaScriptのステートメントで意味のある式に変換できないものが少数あります。break, continue, returです。もしそれらをコードブロックのなかで使用する場合、CoffeeScriptは変換を実行しません。

演算子と別名

CoffeeScriptは==を===、!=を!==、isを===、isntを!==に変換します。なぜなら==演算子はしばしばよくない強制を引き起こし、ほかの言語とことなる意味を持つからです。

!の別名としてnotを使用することもできます。

andを&&、orを||にコンパイルします。

while, if/else, switch/whenステートメントの改行もしくはセミコロンの代わりに式から条件を分離するためにthenを使用することができます。

YAMLなどと同様に、onとyesはtrueと同じであり、offとnoはfalseと同じです。

unlessはifの反対です。

this.propertyのショートカットとして@propertyを使うことができます。

配列中の値に存在するかを確認するためにinを使うことができます。またobjectのキーに存在するかを確認するためにofを使用することができます。

CoffeeScript      JavaScript
is                ===
isnt              !==
not               !
and               &&
or	              ||
true, yes, on     true
false, no, off    false
@, this           this
of                in
in                no JS equivalent
launch() if ignition is on

volume = 10 if band isnt SpinalTap

letTheWildRumpusBegin() unless answer is no

if car.speed < limit then accelerate()

winner = yes if pick in [47, 92, 13]

print inspect "My name is #{@name}"
var volume, winner;

if (ignition === true) launch();

if (band !== SpinalTap) volume = 10;

if (answer !== false) letTheWildRumpusBegin();

if (car.speed < limit) accelerate();

if (pick === 47 || pick === 92 || pick === 13) winner = true;

print(inspect("My name is " + this.name));

存在演算子

JavaScriptで変数の存在チェックを行うのは少々難しい。if(variable)...は0や空文字列、falseで失敗します。CoffeeScriptの存在演算子?は変数がnullもしくはundefinedでなければtrueを返します。これはRubyの"nil?"に類似しています。

数値や文字列を操作したい場合に"||="よりも安全な条件代入として使用することができます。

solipsism = true if mind? and not world?

speed ?= 75

footprints = yeti ? "bear"
var footprints, solipsism;

if ((typeof mind !== "undefined" && mind !== null) && !(typeof world !== "undefined" && world !== null)) {
  solipsism = true;
}

if (typeof speed === "undefined" || speed === null) speed = 75;

footprints = typeof yeti !== "undefined" && yeti !== null ? yeti : "bear";
loadrun: footprints

"?."のアクセス機構はプロパティチェインにおけるnullアクセスによるエラーを吸収することができます。元の値がnullもしくはundefinedを取りうる場合に"."アクセスの代わりに"?."を使用してください。もし全てのプロパティが存在する場合あなたの予想する結果が得られるが、もしチェインが壊れていた場合はundefinedがTypeErrorの代わりに得られます。

zip = lottery.drawWinner?().address?.zipcode
var zip, _ref;

zip = typeof lottery.drawWinner === "function" ? (_ref = lottery.drawWinner().address) != null ? _ref.zipcode : void 0 : void 0;

nullを吸収することはRubyのandand gemやGroovyのsafe navigation operatorに類似しています。

クラス、継承、スーパークラス

JavaScriptのプロトタイプの上にクラス指向の継承を行うためにわかりやすい構文を提供しているライブラリ群(Base2, Prototype.js, JS.Classなど)を使用してもJavaScriptのプロトタイプ継承は少々厄介である。それらのライブラリはシンタックスシュガーを提供するが、組み込みの継承は少しの小さな例外を除けば完全に使用することができます。1つ目の例外は不恰好ななsuper(現在の関数のプロトタイプオブジェクトの実装)の呼び出し、2つ目は不恰好なプロトタイプチェインの設定です。(**どこが不恰好なのかわからない)

プロトタイプに関数を繰り返し加える代わりに、CoffeeScriptは基本的なclass構造を提供します。class構造は一行の代入式のなかでクラスに名前をつけ、スーパークラスをセットし、プロトタイプにプロパティを代入し、コンストラクタを定義します。

コンストラクタは名前を持ち、スタックトレースを便利にサポートします。

class Animal
  constructor: (@name) ->

  move: (meters) ->
    alert @name + " moved #{meters}m."

class Snake extends Animal
  move: ->
    alert "Slithering..."
    super 5

class Horse extends Animal
  move: ->
    alert "Galloping..."
    super 45

sam = new Snake "Sammy the Python"
tom = new Horse "Tommy the Palomino"

sam.move()
tom.move()
var Animal, Horse, Snake, sam, tom,
  __hasProp = Object.prototype.hasOwnProperty,
  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor; child.__super__ = parent.prototype; return child; };

Animal = (function() {

  function Animal(name) {
    this.name = name;
  }

  Animal.prototype.move = function(meters) {
    return alert(this.name + (" moved " + meters + "m."));
  };

  return Animal;

})();

Snake = (function(_super) {

  __extends(Snake, _super);

  function Snake() {
    Snake.__super__.constructor.apply(this, arguments);
  }

  Snake.prototype.move = function() {
    alert("Slithering...");
    return Snake.__super__.move.call(this, 5);
  };

  return Snake;

})(Animal);

Horse = (function(_super) {

  __extends(Horse, _super);

  function Horse() {
    Horse.__super__.constructor.apply(this, arguments);
  }

  Horse.prototype.move = function() {
    alert("Galloping...");
    return Horse.__super__.move.call(this, 45);
  };

  return Horse;

})(Animal);

sam = new Snake("Sammy the Python");

tom = new Horse("Tommy the Palomino");

sam.move();

tom.move();

If structuring your prototypes classically isn't your cup of tea, CoffeeScriptは少しの低レベルな便利機能を提供します。extends演算子は適切なプロトタイプを設定するのに役立ち、そしてコンストラクタ関数間で継承のチェインを作成します。::はオブジェクトのプロトタイプへの短いアクセスを提供します。super()は親クラスの同じ名前のメソッド呼び出します。

String::dasherize = ->
  this.replace /_/g, "-"
String.prototype.dasherize = function() {
  return this.replace(/_/g, "-");
};

最後にクラス定義ははメタプログラミングを可能にする面白い実行可能なコードの塊です。クラスプロパティを"@property: value"を使って代入でき、親クラスで定義されている関数を"@attr 'title', type: 'text'"で実行することができます。なぜならクラス定義のコンテキスト中でthisはクラスオブジェクト自身であるからです。

展開代入

配列やオブジェクト複合されたものから値を展開するために、CoffeeScriptはECMAScript Harmoneysの提案している展開代入を実装しています。配列もしくはオブジェクトのリテラルを値に代入する時、CoffeeScriptは代入の両辺を展開してマッチするものを右辺の変数から左辺に代入します。単純なケースの場合、並列代入のために使うことができます。

theBait   = 1000
theSwitch = 0

[theBait, theSwitch] = [theSwitch, theBait]
var theBait, theSwitch, _ref;

theBait = 1000;

theSwitch = 0;

_ref = [theSwitch, theBait], theBait = _ref[0], theSwitch = _ref[1];

それだけではなく複数の値を返す関数を扱うためにも役立ちます。

weatherReport = (location) ->
  # Make an Ajax request to fetch the weather...
  [location, 72, "Mostly Sunny"]

[city, temp, forecast] = weatherReport "Berkeley, CA"
var city, forecast, temp, weatherReport, _ref;

weatherReport = function(location) {
  return [location, 72, "Mostly Sunny"];
};

_ref = weatherReport("Berkeley, CA"), city = _ref[0], temp = _ref[1], forecast = _ref[2];


展開代入は配列とオブジェクトがネストされたものにも使用できます。これは深くにネストされたプロパティを取り出すのに役立ちます。

futurists =
  sculptor: "Umberto Boccioni"
  painter:  "Vladimir Burliuk"
  poet:
    name:   "F.T. Marinetti"
    address: [
      "Via Roma 42R"
      "Bellagio, Italy 22021"
    ]

{poet: {name, address: [street, city]}} = futurists
var city, futurists, name, street, _ref, _ref2;

futurists = {
  sculptor: "Umberto Boccioni",
  painter: "Vladimir Burliuk",
  poet: {
    name: "F.T. Marinetti",
    address: ["Via Roma 42R", "Bellagio, Italy 22021"]
  }
};

_ref = futurists.poet, name = _ref.name, (_ref2 = _ref.address, street = _ref2[0], city = _ref2[1]);


展開代入はすぷらっとを使って結合することもできます。

tag = "<impossible>"

[open, contents..., close] = tag.split("")
var close, contents, open, tag, _i, _ref,
  __slice = Array.prototype.slice;

tag = "<impossible>";

_ref = tag.split(""), open = _ref[0], contents = 3 <= _ref.length ? __slice.call(_ref, 1, _i = _ref.length - 1) : (_i = 1, []), close = _ref[_i++];

関数束縛

JavaScriptではthisキーワードは動的スコープであり、現在の関数にひもづけられているオブジェクトという意味を持ちます。関数を異なるオブジェクトにコールバックとして渡したり、紐付けた場合、thisの元の意味は失われてしまいます。この動作をよく理解していない場合、"this Digital Web article"は小難しい概要を示してくれます。

=>演算子は関数の定義とその時のthisの値を関数に束縛してくれます。これはPrototype.jsやjQueryのようなコールバックベースのライブラリを使うときに役に立ちます。例えばeashメソッドに渡すためのイテレータ関数を作るときやbind関数を使ってイベントハンドラを使うときです。=>を使って作られた関数はそれが定義されたときのthisのプロパティにアクセスすることができます。

Account = (customer, cart) ->
  @customer = customer
  @cart = cart

  $('.shopping_cart').bind 'click', (event) =>
    @customer.purchase @cart
var Account;

Account = function(customer, cart) {
  var _this = this;
  this.customer = customer;
  this.cart = cart;
  return $('.shopping_cart').bind('click', function(event) {
    return _this.customer.purchase(_this.cart);
  });
};

コールバックの中でもし->を使っていたら、@customerへのアクセスはundefinedになっており、purchase()関数を実行しようとして例外を引き起こします。

クラス定義を使っている場合、=>を使って定義されたメソッドはインスタンスが構築される時に自動的にクラスのインスタンスに束縛されます。

JavaScriptの埋め込み

CoffeeScriptでJavaScriptのスニペットを書き込みたい場合、バッククウォートを使って直接JavaScriptを書きこむことができます。これはほとんど使用されることは無いはずです。

hi = `function() {
  return [document.title, "Hello JavaScript"].join(": ");
}`
var hi;

hi = function() {
  return [document.title, "Hello JavaScript"].join(": ");
};

Switch/When/Else

JavaScriptのSwitchステートメントは少し不格好です。デフォルトケースに落下する自己を避けるために、全てのcaseステートメントの終わりにbreakを必要とします。CoffeeScriptは次のケースへの落下を防ぎます。そしてSwitch文を値を返す代入可能な式へと変換します。構文はswith 条件値, when ケース, else デフォルトケースです。

Rubyのように、CoffeeScriptのswitchステートメントは複数の値をwheに書くことができます。もし値のうちどれか1つでもマッチすれば実行されます。

Try/Catch/Finnaly

Try/catchステートメントはJavaScriptと同じです。(ただし、式として動作します)

try
  allHellBreaksLoose()
  catsAndDogsLivingTogether()
catch error
  print error
finally
  cleanUp()
try {
  allHellBreaksLoose();
  catsAndDogsLivingTogether();
} catch (error) {
  print(error);
} finally {
  cleanUp();
}

連続比較

CoffeeScriptはPythonから連続比較も持ってきています。いくつかの範囲に入っているかどうかのテストを容易にすることができます。

cholesterol = 127

healthy = 200 > cholesterol > 60
var cholesterol, healthy;

cholesterol = 127;

healthy = (200 > cholesterol && cholesterol > 60);

文字列内変数展開、ヒアドキュメント、ブロックコメント

Rubyスタイルの文字列内変数展開がCoffeeScriptには含まれます。""で囲まれた文字列は変数の展開を"#{...}"を使うことで許可しています。''で囲まれた文字列はリテラルとなります。

author = "Wittgenstein"
quote  = "A picture is a fact. -- #{ author }"

sentence = "#{ 22 / 7 } is a decent approximation of π"
var author, quote, sentence;

author = "Wittgenstein";

quote = "A picture is a fact. -- " + author;

sentence = "" + (22 / 7) + " is a decent approximation of π";


複数行の文字列も可能です。

mobyDick = "Call me Ishmael. Some years ago --
 never mind how long precisely -- having little
 or no money in my purse, and nothing particular
 to interest me on shore, I thought I would sail
 about a little and see the watery part of the
 world..."
var mobyDick;

mobyDick = "Call me Ishmael. Some years ago -- never mind how long precisely -- having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world...";
loadrun: mobyDick


ヒアドキュメントは整形もしくはインデントされたテキストをそのまま使用することができます(もしくは"と'をエスケープしたくない場合)。ヒアドキュメントの開始時のインデントレベルは維持されます。つまりコードとインデントを揃えることができます。

html = """
       <strong>
         cup of coffeescript
       </strong>
       """
var html;

html = "<strong>\n  cup of coffeescript\n</strong>";
loadrun: html


""のヒアドキュメントは""の文字列と同じく変数展開が可能です。


生成されたJavaScriptにブロックコメントを入れたい場合があると思います。たとえばファイルの先頭にライセンスを入れたい場合です。ヒアドキュメントと同じ書き方であるブロックコメントは生成されたコードにも書き出されます。

###
CoffeeScript Compiler v1.2.0
Released under the MIT License
###
/*
CoffeeScript Compiler v1.2.0
Released under the MIT License
*/

拡張正規表現

ヒアドキュメントやヒアコメントと同様にCoffeeScriptでは"heregexes"をサポートします。拡張された正規表現は内部のスペースを無視し、コメントを含むことができます。Perlの"/x"を変更したもので、区切り文字は"///"です。拡張正規表現は複雑になりがちな正規表現を見やすくすることができます。

OPERATOR = /// ^ (
  ?: [-=]>             # function
   | [-+*/%<>&|^!?=]=  # compound assign / compare
   | >>>=?             # zero-fill right shift
   | ([-+:])\1         # doubles
   | ([&|<>])\2=?      # logic / shift
   | \?\.              # soak access
   | \.{2,3}           # range or splat
) ///
var OPERATOR;

OPERATOR = /^(?:[-=]>|[-+*\/%<>&|^!?=]=|>>>=?|([-+:])\1|([&|<>])\2=?|\?\.|\.{2,3})/;