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

プログラムdeタマゴ

世界の端っこ

CoffeeScript入門

JavaScript CoffeeScript

この記事が対応するCoffeeScriptのバージョンは1.7です。
基本的には本家サイトCoffeeScriptの内容と同じです。わかりやすいように色々と私が勝手に再構築、追加してはありますが。(日本語訳と紹介されるとちょっと心外ですな(´Д⊂ヽ )
なお、JavaScriptの基礎知識を前提とし、JavaScriptから引き継ぎ重複する内容は解説されません。
あ。後、この記事の文章、CoffeeScriptコード、JavaScriptコードに関する著作権は放棄しています。パブリックドメインと言うそうです。文章、コード自由に引用改変公開しても問題ありません。


最終更新日:2014/1/29
追加:
文字列
演算子など

はじめに:CoffeeScriptのメリットとデメリット

 一般に言われているCoffeeScriptを使うメリットデメリットを載せておきます。
 ま、こんな論争気にせず、使ってみて気に入ったら使い続ければいいし、気に入らなかったらやめればいいだけだと私は思うんだけどね。
メリット:
 JavaScriptに比べて簡潔に書ける
 ==か===かといった問題がない
 クラスが使える

デメリット:
 近い将来廃れる
 ある程度の規模になるとデバッグがしにくい


目次

  1. 秀丸 強調定義ファイル
  2. インストール
  3. コンパイル
  4. コメント
    1. 単一行
    2. 複数行
  5. インデント
  6. 変数
    1. 変数
    2. @修飾子
  7. 数値
  8. 文字列
    1. 文字列
    2. ヒアドキュメント
    3. 式展開
  9. 配列
    1. 配列作成
    2. 範囲指定
      1. ..
      2. ...
    3. 連番の配列を作成
    4. for文を利用した配列生成
    5. 配列要素へのアクセス
    6. 配列の範囲内の要素を取り出す
    7. 配列の範囲内の要素を置換する
    8. 分割代入
  10. オブジェクト(連想配列)
    1. オブジェクト(連想配列)
    2. 分割代入
  11. 演算子など
    1. CoffeeScript固有の演算子等について
    2. 比較演算子の連結
    3. or=,||=,and=,&&=
    4. 存在演算子 ?
      1. 条件式の?
      2. 二項演算子としての?
      3. ?.アクセス演算子
  12. 条件分岐
    1. if,unless,else
    2. 処理が一行しかない時の簡易記法
    3. 3項演算子のif
    4. switch文
  13. ループ
    1. while,until
    2. while,untilは配列を返す
  14. 配列内包 for
    1. 配列内包とは
    2. for文の構文
    3. tips:インデックス変数は各ループ毎にイミュータブル
    4. tips:配列にアクセスするための値を格納する変数を決める
    5. break文,continue文
    6. by
  15. 関数
    1. 関数定義
    2. 引数の初期値
    3. 可変長引数
    4. 関数呼び出し
    5. tips:変数名の衝突
    6. do関数
    7. 関数バインディング
  16. クラス
    1. クラスの宣言
    2. prototypeメンバの追加
    3. prototypeメンバへのアクセス
    4. new演算子
    5. コンストラクター
    6. クラスの拡張
    7. tips:拡張したクラスのinstanceof
    8. super
    9. tips:スーパークラスがオーバーライドされた関数を呼ぶときは?
    10. tips:クラスの変数、関数
    11. tips:クラスの関数のオーバーライドについて
    12. tips:private staticな変数、関数
  17. JavaScriptの挿入
  18. 正規表現
    1. 単一行
    2. 複数行
  19. 例外
    1. 例外処理
    2. throwも式
  20. Cakefile


秀丸 強調定義ファイル

誰かがすばらしい定義ファイルを作ってくれる日を願って………
Theテキトーな自作定義ファイル。
COFFEE.hilight 直強調定義ファイル
COFFEEs.hilight 直複数コメント定義ファイル
COFFEEoutline.hilight 直解析定義ファイル


これらのファイルをnodamushiの許可無く改変、再配布することを禁じません。
そのような事態が発覚した場合、配布人へ謝意を申し上げに参ります。
………ま、そんな希有な人はいないだろうけど。


インストール

1.node.jsのインストール
2.コマンドプロンプトでnodeとタイプし、node.jsが起動することを確認し、終了する
 (動かない場合はPATHの確認)
3.コマンドプロンプトで「npm install -g coffee-script」と入力
4.coffeeとタイプして動作を確認。
 (動かない場合はPATHの確認)

Macなどでも1と3の手順で良いはず。


コンパイル

alert "Hello"

上記の内容をtest.coffeeとして適当なフォルダに保存。
コマンドプロンプトを起動して、フォルダに移動し、

coffee -cb test.coffee

とタイプ。test.jsが同じディレクトリに作成されます。
 coffeeコマンドのオプション-cjsファイルとしてコンパイル結果を出力するという意味。
 -b全体を関数として囲わないようにします。
 同じファイル名+jsという名前ではなく、別なディレクトリに別な名前で保存したかったら-oオプションを指定する。

 オプション一覧

オプション 効果
-c
--compile
JavaScriptファイルとして結果を出力する。
ファイル名は元のcoffeescriptファイルと同名。以下の例はsrcフォルダ内にあるcoffeeファイルをコンパイルする。
coffee -c src/*.coffee
-i
--interactive
REPLを起動。rlwrapとあわせると良い
-o
--output
JavaScriptファイルの出力ディレクトリの指定。cオプションやwatchとあわせて使う。以下の例はsrcフォルダ内にあるファイルをまとめてlibフォルダに出力する。
coffee -o lib/ -c src/
-j
--join
コンパイル前に、全てのスクリプトを渡された順番に結合する。
大きなプロジェクトに使うと便利だそうです。
バージョン1.1.0から結合後のファイル名を指定できるようになりました。
例)coffee -j out.js -c test.coffee test2.coffee
-w
--watch
coffeeスクリプトの修正時間を監視し、変更されたらコンパイルし直す
-p
--print
出力結果を標準出力にプリントする。
-l
--lint
JavaScript Lintがインストールされている場合、コンパイル結果をjslを使ってチェックする。
wオプションと伏せて使うといいらしい。
-s
--stdio
標準入力からcoffeeスクリプトを受け取り、標準出力へコンパイル結果を出力する。
-e
--eval
コマンドラインから直接入力されたCoffeeScriptをコンパイルして実行し、結果を表示する
coffee -e "puts num for num in [10..1]"
-r
--require
スクリプトをコンパイルまたは実行する前にライブラリーを読み込む。
-b
--bare
セーフティ関数で囲わない。
簡単に言えば、大域環境に登録するようになる。
-t
--tokens
CoffeeScriptをパースする代わりに、Lexで字句解析し、トークンストリームを出力。
-n
--nodes
CoffeeScriptをパースする代わりに、Lexで字句解析し、構文木を出力。
--nodejs 何か--debugや--max-stack-sizeとかのフラグでNodejsと連携できるらしいよ。
誰か詳細求む


コメント

単一行

#以降の文字がコメントとして扱われます

#〜〜〜

 

複数行

###で囲った部分がコメントになります。
ファイル先頭に書いた複数行コメントはそのままコンパイルファイルに出力されます。

###
〜〜〜
###


インデント

pythonライクな言語なので、インデントの深さでブロックが生成されます。

処理群1
	処理群2
		処理群3
	処理群4

こんな感じのインデントになっていたらJavaScriptでは

処理群1
{
	処理群2
	{
		処理群3
	}
	処理群4
}

このような関係になる、という大雑把なイメージを持っておいてください。


変数

CoffeeScriptには変数宣言はありません。

変数名=………

の様に初めて記述したところから変数が宣言された物と見なされます

 

@修飾子

CoffeeScriptでは変数名の前に@を付けた場合、現在のオブジェクトが保有する変数を指すようになります。
(だいたいthis.がついたのと同じ扱いになります。)

@param=1
↓
this.param=1

ただし、後述する関数バインドなどでは、@が単純にthisに変換されない場合もあります。
一応、thisキーワードを使うことも可能ですが、@を使っておきましょう。なお、例えば関数チェーンなどで自分を返したいときに、@単体で使うこともできます。


数値

 JavaScriptでは数値の書き方として

  • 10進数(例:11)
  • 8進数(例:013)
  • 16進数(例:0xb)

があります。CoffeeScriptではv1.1.3からこれらに加え2進数表記が可能になりました。

0b〜

の形で表記します。以下の例は全て変数に11を代入しています。

a=11
b=013
c=0xb
d=0b1011


文字列

文字列

 文字列はダブルクォーテーション"とシングルクォーテーション'のどちらかで囲います。文字列は、下記例の様に複数行にわたって文を書けます。

 改行は文字列に適応されませんが、バージョン1.7から半角空白が挿入されるようになりました。

また、行頭行末の空白は無視されます。


multistring="複数行文字列は
改行すると空白が入ります
←この空白やこの空白は→
無視されるンゴ
"

multistring='
 ←全角空白ですが→    
無視されるンゴ
'

↓コンパイル

multistring = "複数行文字列は 改行すると空白が入ります ←この空白やこの空白は→ 無視されるンゴ";
multistring = '←全角空白ですが→ 無視されるンゴ';

シングルとダブルの違いは式展開において違いが出ます。

 

ヒアドキュメント
'''
〜〜〜
'''

とするとヒアドキュメントになります。

 

式展開(String Interpolation)

ダブルクォーテーション内部では以下の様に書くと式の展開が行われます。

#{式}

シングルクォーテーションでは式は展開されません。
式には変数名や、1+2などの数式を入れられます。

"A picture is a fact. -- #{ author }"
↓コンパイル
 "A picture is a fact. -- " + author;

 

サンプル


sogeb='''
                       ヘ(^o^)ヘ いいぜ
                         |∧  
                     /  /
                 (^o^)/ てめえが何でも
                /(  )    思い通りに出来るってなら
       (^o^) 三  / / >
 \     (\\ 三
 (/o^)  < \ 三 
 ( /
 / く  まずはそのふざけた
       幻想をぶち殺す
'''


cat="
吾輩は
猫である
気がする
"



alert "cat=#{cat}\nsogeb=#{sogeb}"
alert 'cat=#{cat}\nsogeb=#{sogeb}'
コンパイル結果。

var cat, sogeb;
sogeb = '                      ヘ(^o^)ヘ いいぜ\n                        |∧  \n                    /  /\n                (^o^)/ てめえが何でも \n               /(  )    思い通りに出来るってなら\n      (^o^) 三  / / >\n\     (\\ 三\n(/o^)  < \ 三 \n( /\n/ く  まずはそのふざけた\n      幻想をぶち殺す';
cat = "吾輩は 猫である 気がする";
alert("cat=" + cat + "\nsogeb=" + sogeb);
alert('cat=#{cat}\nsogeb=#{sogeb}');


配列

配列作成

CoffeeScriptでの配列宣言はJavaScriptと同様に宣言することが出来ます。
また、改行も区切り文字として認識されます。

arr=[1,2,3]
arr=[1 
2
3]

 

範囲

CoffeeScriptでは範囲を指定する構文があります。

..
a..bはa以上b以下、もしくはa以下b以上を表します。



...
a...bはa以上b未満、もしくはa以下bより大を表します。

 

連番の配列を作成

範囲の指定によりaからb未満(もしくはbより大)の連続する値を持つ配列を簡単に生成できます。

[a...b]

「..」と2点の場合はaからb以下(もしくはb以上)の連続する値を持つ配列になります。

 

for文を利用した配列生成

for文(後述)を利用すると[1,3,5,7………101]といった配列も容易に生成できます。

arr = (num for num in [1...102] by 2)

 
サンプル

arr=[1,2,3,4,5]
arr=[1
2
3]
arr=[1...10]
arr=[12..10]
arr=(num for num in [1...102] by 2)

コンパイル結果

var arr;
arr = [1, 2, 3, 4, 5];
arr = [1, 2, 3];
arr = [1, 2, 3, 4, 5, 6, 7, 8, 9];
arr = [12, 11,10];
arr = (function() {
  var _results;
  _results = [];
  for (num = 1; num < 102; num += 2) {
    _results.push(num);
  }
  return _results;
})();

 

配列要素へのアクセス

JavaScript同様です。

 

配列の範囲内の要素を取り出す

配列ARRの添え字がaからbまでの要素を持つ配列arrなどを簡単に作成できます。


arr = ARR[a..b] #a以上b以下の要素の取り出し
arr = ARR[a...b] #a以上b「未満」の要素の取り出し
arr = ARR[a..]#a以上の要素の取り出し
arr = ARR[..]#クローンの作成(ver1.3.1以降)


コンパイルすると

var arr;
arr = ARR.slice(a, (b+1) || 9e9);
arr=ARR.slice(a,b);
arr=ARR.slice(a);
arr=ARR.slice(0);

 

配列の範囲内の要素を置換する

[a..b]の記法は取り出す以外にもその範囲の要素に値を代入することも出来ます。


a=[1..10]
a[2..3]=[10,11] #要素2が10に、要素3が11になる
a[4...6]=[10,11] #要素4が10に、要素5が11になる

 

分割代入

Perlを知っている方なら関数の引数を受け取るときに
($a,$b,$c)=@_
という風に、左辺の配列に入れた変数に右辺の配列の値を順に書き込めることをご存じかと思います。CoffeeScriptでも同じ事が出来ます。


[a,b,c]=[10,20,30] #変数aに10,変数bに20、変数cに30を代入
コンパイル

var a, b, c, _ref;
_ref = [10, 20, 30], a = _ref[0], b = _ref[1], c = _ref[2];

また、左辺の配列に入れる変数の一つだけ「...」を後ろにくっつけることが出来ます。
これを付けた変数は適切な長さを持つ配列が代入されます。

[a,b,c...,d]=[1...10]

コンパイル

var a, b, c, d, _i, _ref;
var __slice = Array.prototype.slice;
_ref = [1, 2, 3, 4, 5, 6, 7, 8, 9], a = _ref[0], b = _ref[1], c = 4 <= _ref.length ? __slice.call(_ref, 2, _i = _ref.length - 1) : (_i = 2, []), d = _ref[_i++];

これらは以下のように代入する値に配列の範囲指定や、深いネスト構造をしていてもきちんと代入がなされます

x = [11..20]
[ a, x[1...3], b ] = [1..10]

[ a, [ b, c...], [d], e] = [ 1, [1...10], [10..20], 5]


また、上のコンパイル結果を見ても分かるようにいったん_refという名前の変数に配列を入れてからa,b,cに値をコピーしているので、以下のような二つの値の交換も安全に実行できます。

a=10
b=0
[a,b] = [b,a]

コンパイル

var a, b, _ref;
a = 10;
b = 0;
_ref = [b, a], a = _ref[0], b = _ref[1];


オブジェクト(連想配列)

JavaScriptとほぼ同様です。{}は省略できます。「,」を記入せずにインデントを利用して作成することも出来ます。


obj={test:"ok",test2:"false"}
obj=
test:"ok"
test2:"false"
コンパイル

var obj;
obj = {
  test: "ok",
  test2: "false"
};
obj = {
  test: "ok",
  test2: "false"
};

要素にはJavaScript同様にobj.testやobj["test"]でアクセスできます。

 

分割代入

連想配列でも配列の時のような分割代入が行えます。
連想配列のキーと同じ名前の変数に同じ名前のキーの値を代入することができます。

「キー名:変数名」を指定すると、そのキーに対応する要素が変数名に格納されます。

配列の時同様に深いネスト関係にあっても取得可能です。


object=
name: "nodamushi"
age : "19++++++++"
sex : "♂"
phone:
tell:"090-9876-5432"
{age,name}=object #nameとageを取得
{sex:seibetsu}=object #seibetsu変数にsexを代入
{phone : {tell}}=object #phoneのオブジェクト内にあるtellを取得。
コンパイル

var age, name, object, seibetsu, tell;
object = {
  name: "nodamushi",
  age: "19++++++++",
  sex: "♂",
  phone: {
    tell: "090-9876-5432"
  }
};
age = object.age, name = object.name;
seibetsu = object.sex;
tell = object.phone.tell;

(謝辞:id:mooz様:間違いの指摘ありがとうございました。)


演算子など

CoffeeScript固有の演算子等について

CoffeeScriptJavaScript
is,=====
isnt,!=!==
not,!!
and,&&&&
or,||||
true, yes, ontrue
false, no, offfalse
@, thisthis
ofin
inno JS equivalent
?no JS equivalent
**Math.pow(a,b)
//Math.floor(a / b)
%%(a % b + b) % b
version1.7から「**」、「//」、「%%」が追加されました。順に、累乗を計算する演算子、割り算の商を整数で返す演算子、あまりを正の数で返す演算子です。あまりを正の数で返すというのは「-3%5=-3」に対して「-3%%5=2」となります。
JavaScriptの比較演算子「==」に対応する演算子は無いようです。
 

比較演算子の連結

pythonからの輸入で比較演算子を以下のように連結することが出来ます。

a > b > c

 

or=,||=,and=,&&=

普通「a += b」と記述することは「a = a + (b)」という処理と同じ事になりますが、or=,||=,and=,&&=といった比較演算子の場合以下のように展開されます。

a or= b
↓
a or (a=b)

aにa or bの値が入るのではなくaにはbが入るので注意してください。


 

存在演算子 ?


条件式の?
?という演算子は存在しているか(nullやundefinedでないか)を返す演算子です。


if a? #変数aが存在しているなら続く処理を行うという意味になる


これだけだと単にnullやundefinedの時falseでそれ以外ならtrueを返すように聞こえますが、そのほかの機能も有したマルチな演算子です。

 

二項演算子としての?

a?b

二項演算子として?を使うと上の式は次のような意味になります。
aが存在しているならばaを、そうでなければbを返す


a = b ? 1 #bがあればaにbを、そうでなければaに1を
a ?= 1 #aが無ければaに1を代入する。

 

?.アクセス演算子
?.演算子はセーフなアクセス演算子です。
JavaScriptは実行してみないと、アクセスする要素が存在しているかどうか分かりません。
それが問題となるとき?.アクセス演算子が役に立ちます。
a?.baが存在するならばaのbにアクセスする、という意味になります。udefinedの要素にアクセスするエラーが発生しません。
?.の形には.の代わりに関数の引数である()を入れた?()という形もあります。


obj={a:()->ob={tset:{target:"ok"}}}
target=obj.a?().test?.target;
#testはないのでundefindeが返ります(あるのはtsetなの)
コンパイル

var obj, target, _ref;
obj = {
  a: function() {
    var ob;
    return ob = {
      tset: {
        target: "ok"
      }
    };
  }
};
target = typeof obj.a === "function" ? (_ref = obj.a().test) != null ? _ref.target : void 0 : void 0;


条件分岐

if,unless,else

ifは渡された条件式が真の時該当する処理を実行する。
unlessは渡された条件式が偽の時該当する処理を実行する。
elseはifもしくはunlessが成立しなかったときに実行される。

if文、unless文は条件式との区別がつかないので、条件式の後に実行したい処理を記述することは出来ない。
else文に関しては同じ行に実行する処理を書いても良い。(ただし、その場合複数行にわたるブロックは生成できないので注意)


if !a?
alert 1
else unless a is b # else if a isnt bとおなじ
alert 2
コンパイル

if (!(typeof a != "undefined" && a !== null)) {
  alert(1);
} else if (a !== b) {
  alert(2);
}

 

処理が一行しかない時の簡易記法

上の例のように処理が一行しかないときはわざわざ改行して、インデントを下げて記述なんて面倒くさいですね。

簡単に書く方法があります。

  1. 処理と分岐条件の間にthenを入れる
  2. ifを後ろに書く。(※else文の追加は出来ない。)


if a
alert 1
↓簡略化
if a then alert 1
alert 1 if a
 

3項演算子のif

if 〜 then 〜 elseの形は3項演算子としても使えます

a = if b then 1 else 2

JavaScriptで一般的な?を使った三項演算子は、?を存在演算子にしてしまった為使えません。


switch文

CoffeeScriptにおけるswitchでの分岐はcase、defaultではなく、whenとelseで書きます。
when節で処理が一行のみで多段組にしたくないときはthenを使って一行にまとめられます。

case 1:
case 2:
  処理

JavaScriptでは上のように書き並べるところは

when 1,2

と,区切りで書き並べます。


switch val
when 1
………
when 2 then ………
when 3,4,5 #いくつかをまとめたいときは,区切り
………
else
………


ループ

while,until

CoffeeScriptでは単純なループはwhileを使います。

while 条件式
	処理


ここでも

処理 while 条件式

という簡略形の構文が使えます。

また、whileではJavaScript同様にbreakやcontinue文などの制御構文が使えます。

while not(条件式)と同じ意味になるuntilというループ構文もあります。
個人的な感想だけど、untilとかunlessはPerlで結局全然使わなかったなぁ………

 

whileは配列を返す

whileループはある種一つの関数の様なものとして扱われ、その返値は配列になります。
以下の例を見てみましょう

f=->
	t = 10
	while t-=1
		t*2

(※ ->は無引数の関数を定義するという意味です。)
一見何もしない関数を定義したようですが、これをコンパイルするとこうなります。

var f;
f = function() {
  var t, _results;
  t = 10;
  _results = [];
  while (t -= 1) {
    _results.push(t * 2);
  }
  return _results;
};

このように_resultsという配列を返しています。
whileで代入される配列の要素は処理の一番最後の結果になります。

このためwhile文で終わる関数は必ず配列を返すようになります。while文で終わるが、本当は何も返さない関数の最後には明示的にnullを書かなければなりません



ただし以下のような場合は、配列を返す理由がない(受取手がいない)ので配列を返しません。

t=10
while t-=1
	t*2
var t;
t = 10;
while (t -= 1) {
  t * 2;
}


配列内包 for

配列内包とは

CoffeeScriptにおけるfor文は配列内包という、配列の要素を順に取り出していって各要素について何らかの操作をし、新たな配列に格納するという、配列と連想配列操作の構文になっています。

map関数と言ったら理解が早いかもしれません。
ただし、while同様コンパイラが新たな配列に格納する必要がないと判断できる場合(返ってくる配列の受け取り手が以内場合)は新たな配列を作成しません。

 

for文の構文

for文は配列と連想配列に対して使うことが出来ます。
新たな配列に格納する値は一回の処理で一番最後に行われる処理の返値や変数になります。

1.配列に対しfor文を使う場合


for 配列の要素を代入する変数 in 配列
処理

処理が一行だけの時は以下のように簡略化して書くことも出来る。


処理 for 配列の要素を代入する変数 in 配列


2.連想配列に対しfor文を使う場合


for キーを代入する変数,要素を代入する変数 of 連想配列
処理

なお、要素を代入する変数は省略することが出来ます。
処理が一行だけの時は以下のように簡略化して書くことも出来る。


処理 for キーを代入する変数,要素を代入する変数 of 連想配列



tips:インデックス変数は各ループ毎にイミュータブル

バージョンが1.3.1からはインデックスの変数を変更してもループに影響が出ません。私の語学力では言葉で説明しにくいので、以下のソースコードを見て貰った方が早いでしょう。


for i in [0...10]
i=i*i
ループの処理の中でiの値を書き換えています。これをコンパイルすると以下の様になります。

var i, _i;
for (i = _i = 0; _i < 10; i = ++_i) {
  i = i * i;
}

この様に実際にループの終了判定は変数_iを用いているので、iの値を処理中に書き換えても、各ループ開始時のiの値は順に0から9まで増えていきます。




tips:配列にアクセスするための値を格納する変数を決める

for文で配列の要素にアクセスするためのキー(数値)を格納する変数は_iなどコンパイラ側が用意してくれますが、以下の様にして自分で定義することが可能です。


arr = ['a','b','c']
for str ,number in arr
この様に、配列の要素を代入する変数の次にもう一つ変数を宣言すると、arrにアクセスするための変数がnumberになります。コンパイルすると

for (number =0, _len=arr.length; number < _len; number++){
    str = arr[number];

といった感じになります。
これを利用すれば、今何度目のループか?なども取得できるでしょう。





break文,continue文

javascriptのforループ同様にbreak文とcontinue文が使用できます。

 

by

for文は配列の要素を順に取り出していくが、飛び飛びに取り出していくこともできる。
そのための構文がbyだ


for 変数名 in 配列 by 次の要素までの距離
デフォルトではbyの次の要素までの距離が1である。
たとえば、2ずつ配列の要素をとっていきたいなら

for t in arr by 2

となるし、要素の値分飛ばしたいなら

for t in arr by t

となる。

例:


#数列1,3,5,7,9の総和の数列を作成する。
k=0
sums = k+=num for num in [1..10] by 2
#sums=1,4,9,16,25となる

#値が"ok"のkeyだけ3つ返す
hash= java:"ok", ruby:"no", perl :"ok", javascirp :"ok",python:"ok"
k=0
oks = for language, value of hash
if "ok" isnt value then continue
break if k is 3
k++
languege



関数

関数定義
関数名=(変数,変数)->
	処理

返値は最後に処理した値になる。*1(関数の最後の行とは限らない。)
明示的にreturnで関数から抜けることも出来る。
引数がない場合は括弧は省略できる。

 

引数の初期値

関数で受け取る変数は初期値を設定することが出来る

(x=0,y=1)->
	………

x,yが渡されなかった場合x=0,y=1が代入される。

 

可変長引数

...を変数名の後に加えることで、可変長引数となり、それ以降の引数を配列として受け取れる。

(x,y...)->
	………

 

関数呼び出し

関数の呼び出しは基本的には括弧はいらない

関数名 引数,引数………

が、関数に引数が無いときには括弧が必要である。
また、二つの関数の結果を加えたいとき

関数名 引数 + 関数名 引数

などの様に書くと

関数名(引数+関数名(引数))

の様に扱われてしまうので、括弧を付ける。なお、関数名と括弧の間にスペースを入れてはいけない。

関数名(引数) + 関数名 引数
サンプル
sqr=Math.sqrt
square=(x)->x*x
hypot=(x=0,y=0)->
	temp=square(x) + square y
	sqr temp
add=(x,y...)->
	x+=temp for temp in y
	x
add 1,hypot(1,2),hypot 2,3


コンパイル結果

var add, hypot, sqr, square;
var __slice = Array.prototype.slice;
sqr = Math.sqrt;
square = function(x) {
  return x * x;
};
hypot = function(x, y) {
  var temp;
  if (x == null) {
    x = 0;
  }
  if (y == null) {
    y = 0;
  }
  temp = square(x) + square(y);
  return sqr(temp);
};
add = function() {
  var temp, x, y, _i, _len;
  x = arguments[0], y = 2 <= arguments.length ? __slice.call(arguments, 1) : [];
  for (_i = 0, _len = y.length; _i < _len; _i++) {
    temp = y[_i];
    x += temp;
  }
  return x;
};
add(1, hypot(1, 2), hypot(2, 3));

 



tips:変数名の衝突

 スコープはRubyと同じらしい。(私実はRuby使えないけど。)
 上サンプルコードでは、関数内部で使っているtempという変数がグローバル領域で定義されていないので、コンパイル後は関数内部でvar tempと変数宣言されています。しかし、この関数よりも前にグローバル領域に変数tempが宣言されている場合、コンパイラはグローバル変数tempと関数内部のtempを同じ物と見なしてしまい、ローカル変数宣言は行われません。
 ローカル変数かどうかをCoffeeScriptだけで指定する方法はないらしい*2ので名前の衝突が起こらないようにしなければならない。
 関数をjs風に書いてバッククォーテーションで囲むとそのまま出力されるので、どうしてもローカル変数を宣言したいならこれを使うしかない模様。バージョンが1.3.1以降ならば、do関数を利用することも考えられると思います。(関数にダミー引数を入れて、それをローカル変数として利用する方法も考えられます。しかし、可変長因数が使えなくなりますし、ソースコードの可読性が下がってしまうので、止めた方が良いと思います)

 

do関数

JavaScriptではループの中で、変数を保護するためにクロージャーを使うことが一般的なんだそうです。へー、知らなかった。
さて、「do」というキーワードを用いて関数を定義すると、定義をしただけで自動的に実行をしてくれます。


for i in [0..9]
do(i)->
alert(i*i)
このソースコードをコンパイルすると、

var i, _fn, _i;
_fn = function(i) {return alert(i * i);};
for (i = _i = 0; _i <= 9; i = ++_i) {
  _fn(i);
}

この様に自動的に作成した関数を呼び出してくれています。

また、バージョン1.3.1からdo関数のネームスペースが柔軟になりました。

次のCoffeeScriptを考えます。このdo関数はxの初期値としてobj要素が"obj"というオブジェクトを与えています。


x={obj:1}
do(x={obj:"obj"})->
alert(x.obj)
これを1.3.1以前のバージョンでコンパイルすると

var x;
x = { obj: 1};
(function(x) {
  if (x == null) {
    x = {obj: "obj"};
  }
  return alert(x.obj);
})(x);

この様になります。xはnullでないので、実行すると「1」がアラートされます。

次に1.3.1以降でコンパイルするとこの様になります。

var x;
x = { obj: 1};
(function(x) {
  return alert(x.obj);
})({ obj: "obj"});

この場合、実行すると"obj"がアラートされます。外側のxと内側のxは別物だと言うことをよりはっきりさせたと言うことなのでしょう。

 

関数バインディング

addEventListenerやjQueryのbindなどでイベントに関数を渡すとき、イベントに関数を渡した場所のオブジェクトの持つ変数vにアクセスしたくてうっかりthis.vなんてやって失敗しまくってないだろうか。(私だけ?)
CoffeeScriptではその煩わしさを解消してくれるすばらしい関数定義の方法がある。

()=>
	処理

という構文だ。通常の関数定義が「->」に対して関数バインディングでは「=>」となっただけである。
この構文内ではthisや@修飾子が指すオブジェクトが関数を定義したオブジェクトになる。

サンプル


func =->
@parameta="test"
@add =(t)->t+t
doc=document.getElementById("test")
doc.addEventListener 'click',(e)=>
doc.innerHTML = @add @parameta
alert(doc.innerHTML)
,false
上の例では@parametaや@addは同じ物を指している。
コンパイルするとこのようになる。

var func;
func = function() {
  var doc,_this = this;
  this.paraeta = "test";
  this.add = function(t) {
    return t + t;
  };
  doc = document.getElementById("test");
  return doc.addEventListener('click', function(e) {
    doc.innerHTML = _this.add(_this.parameta);
    return alert(doc.innerHTML);
  }, false);
};

addEventListenerに渡している無名関数内の@adや@parametaがthisではなく、_thisに変更されていることがわかる。


クラス

JavaScriptのクラスは使いにくい。特に継承が。(というか、実際はクラスないけど…)
super関数を呼び出すとか結構しんどいよね。

CoffeeScriptにはそんな問題を一気に解決してくれるすばらしいクラス構文があります。

 

クラスの宣言

クラスの宣言は以下のようにして行います

class クラス名

 

prototypeメンバの追加

JavaScriptでFunc.prototype={………}と書いていた部分は以下の様に書きます

class クラス名
	member : ………
	member2:………

オブジェクト(連想配列)の構文にクラス宣言がついただけですね。


また、以下の様にも追加できます。


クラス名::member = ………

 

prototypeメンバへのアクセス

以下の様にアクセスできます。


クラス名::member = ………
クラス名::member()

インスタンス内からはJavaScriptでやるように、thisを使ってアクセスすると良いです。
thisの代わりに@修飾子を使うとタイプが楽です。

 

new演算子

クラスのインスタンスを生成するにはJavaScript同様にnew演算子を使います。

 

コンストラクター

クラスのインスタンスを生成する際に実行される関数、コンストラクターは以下の様に定義します。

class クラス名
	constructor:(引数)->
		処理

クラス名::constructorで宣言してはいけません。
後から追加するとコンストラクターが呼ばれません。

 

クラスの拡張

extendsキーワードを使うことでクラスを拡張することが出来ます。便利だね〜

class クラス名 extends  スーパークラス名

 

tips:拡張したクラスのinstanceof

「インスタンス instanceof クラス名 」でインスタンスがクラスのインスタンスかどうかを調べられます。
このクラス名は拡張したクラス名でもきちんとtrueが返ってきます。


class Test
class Test2 extends Test

t = new Test2
t instanceof Test2 #true
t instanceof Test #true

 

super

スーパークラスと同じ名前関数を定義することをオーバーライドといいます。
オーバーライドした関数からスーパークラスの関数をsuperで呼ぶことが出来ます。


class Person
constructor : (@name="",@age=0)->
say:()->"my name is #{@name}"

class Nodamushi extends Person
constructor: ->super("nodamushi",19)
say:()-> super() + "I'm always #{@age}years old"

noda = new Nodamushi
alert noda.say()
#↑my name is nodamushi I'm always 19 years oldとアラート

 

tips:スーパークラスがオーバーライドされた関数を呼ぶときは?

C++風に言うならば、CoffeeScriptのクラスの関数には全てにvirturalがついているような物です。
以下の様に定義されているとき、Testクラスの関数aはTest2クラスのインスタンスから呼ばれたとき、Test2クラスでオーバーライドした関数bを呼び出します。


class Test
a:->b()
b:->alert("ok")
class Test2 extends Test
b:->alert("(´・∀・`)") #オーバーライド

t = new Test2
t.a() #(´・∀・`)がアラートされます。




tips:クラスの変数、関数

prototypeメンバではなくクラスに属する変数や関数を定義するには以下の様にする。


class Test
@test: 1
@func: ->
コンパイル結果

var Test;
Test = (function() {
  function Test() {}
  Test.test = 1;
  Test.func = function() {};
  return Test;
})();



tips:クラスの関数のオーバーライドについて

v1.1.3からクラスに属する関数をオーバーライドしたときにsuperで呼び出すことが可能になりました。


class T
@f:->alert("a")

class T2 extends T
@f:->super

#v1.1.3からは「a」がアラートされる
T2.f()

コンパイルすると

T2.f = function() {
return T2.__super__.constructor.f.apply(this, arguments);
};
と「constructor」(すなわちfunction T)が入るようになり、呼び出せるようになっています。以前のバージョンだとconstructorが入りません。
 

tips:private staticな変数、関数

Javaでいうクラスだけが使える変数や関数、すなわちprivate static〜みたいなのは以下の様に定義すればいいようだ。


class Test
privatevalue=1
privatefunc=(v)->
alert v

a:->privatefunc(privatevalue)
#@やらthisはいらない。
コンパイル結果

Test = (function() {
  var privatefunc, privatevalue;
  function Test() {}
  privatevalue = 1;
  privatefunc = function(v) {
    return alert(v);
  };
  Test.prototype.a = function() {
    return privatefunc(privatevalue);
  };
  return Test;
})();


JavaScriptの挿入

バッククォーテーションでくくった部分はJavaScriptとして認識され、そのままjsファイルに書き込まれます。

`
var k=12;
function test(){return "ok";}
`

コンパイル結果

var k=12;
function test(){return "ok";}
;

これを利用すると関数内でローカル変数を明示的に宣言することが出来ます

k=12
f=()->
	`var k;`
	k=5
f()
alert k 

コンパイル結果

var f, k;
k = 12;
f = function() {
  var k;;  return k = 5;
};
f();
alert(k);

出力結果があんまり綺麗に整形されてないね………。


正規表現

単一行

単一行の正規表現はJavaScriptの正規表現と変わりません。
正規表現の使い方は下記のサイト等を参考にしてください。
正規表現(RegExp

/パターン/オプション

 

複数行

///で囲むことで複数行にわたってパターンを記述できます。改行や空白は無視されます。

///
パ タ
  ー #半角空白またはタブを#の前に書けばコメントの記入も可
ン
///g

↓コンパイル
/パターン/g


例外

例外処理

JavaScriptと同様です。

try
	処理
catch error
	処理
finally
	処理




throwも式

1.2.0からthrowそれ自身も式として扱われるようになりました。具体的には


a=throw("throw")
こんなソースコードが通るようになりました。コンパイルすると以下の様になります。

a = (function() {
  throw "throw";
})();



………で、これどこで使うんすか?


Cakefile

CoffeeScriptにはMakefileの様な機能を持った物を作る事が出来ます。
下記の記事で解説を書きました。
Cakefileの使い方

*1:

Everything is an Expression (at least, as much as possible) You might have noticed how even though we don't add return statements to CoffeeScript functions, they nonetheless return their final value.

*2:

This behavior is effectively identical to Ruby's scope for local variables. Because you don't have direct access to the var keyword, it's impossible to shadow an outer variable on purpose, you may only refer to it. So be careful that you're not reusing the name of an external variable accidentally, if you're writing a deeply nested function.
だそうです。