こんにちは。毎年花粉症になりかけている飯塚です。
最近のNode.jsの普及などによってサーバサイドJavaScript界隈が盛り上がっています。そんなホットなJavaScriptをラクにかつエレガントに書くためのCoffeeScriptという言語をチュートリアル風に紹介します。
何かしらのライブラリを自分で書く程度にJavaScriptで開発している人は絶対に使ったほうがいいと思います。JavaScriptを知っていれば1-2時間程度で十分駆使できるようになります。
(2011/6/28:@m_satyr様にご指摘頂いた箇所を修正しました。)
作者はDocumentCloudのJeremy Ashkenas氏です。CoffeeScriptはSCSSなどと一緒にRails 3.1で公式にサポートされる予定です。
CoffeeScriptのソースはJavaScriptに比べて見た目がすっきりするため、クラス数の多くなる大規模開発では特に有効です。
Node.jsの実行環境とnpm(パッケージマネージャ)が必要なので、入っていない場合はまずインストールします。インストール方法はこちらの記事などを参考にしてください。
もしインストールせずに試してみたい場合、CoffeeScriptのサイトに行き、上部のメニューから「TRY COFFEESCRIPT」をクリックするとインタプリタが使えます。左のスペースにCoffeeScriptを入力すると右のスペースにJavaScriptが出てきます。
npmがインストールできたら以下のコマンドでCoffeeScriptをインストールします。
$ npm install coffee-script
まずは Hello World を作ります。
hello = -> console.log("Hello World!")hello()
上記のソースコードをtest.coffeeというファイル名で保存します。
それを以下のようにcoffeeコマンドでJavaScriptにコンパイルします。
$ coffee -c test.coffee
同じディレクトリにtest.jsが生成されますので、これをNode.jsで実行します。
$ node test.jsHello World
test.jsの中身を覗くと以下のようになっています。
(function() { var hello; hello = function() { return console.log("Hello World!"); }; hello();}).call(this);
綺麗にJavaScriptに変換されています。
また、test.jsを作らずにcoffeeコマンドで直接実行することもできます。
$ coffee test.coffeeHello World
Hello Worldスクリプトから見て取れるように、CoffeeScriptには以下の特徴があります。
var
での変数宣言が不要->
を使う引数を取る関数の場合、()
を省略して書くこともできます。
console.log("Hello World!")console.log "Hello World!"
これらのコンパイル結果はどちらも同じです。
coffeeコマンドには主に以下のようなオプションがあります。
-c
(--compile
)
.coffee から .js にコンパイルします。
-b
(--bare
)
外側の(function(){ ... }).call(this);
を取り除きます。
-w
(--watch
)
ソースファイルを監視して、ファイルが変更されるたびにコンパイルを実行します。
オプションなしでcoffeeコマンドを起動した場合、インタラクティブなインタプリタが起動します。
->
を使って定義します。引数を取る場合は->
を(var1, var2) ->
のようにして引数を並べます。関数内の最後の値が自動的にreturnされます。
CoffeeScript:
square = (x) -> x * x
↓コンパイル結果
square = function(x) { return x * x;};
複数行にわたる場合はインデントを使います。
fib = (n) -> if n < 2 n else fib(n - 1) + fib(n - 2)
1行で書くと以下のようになります。
fib = (n) -> if n < 2 then n else fib(n - 1) + fib(n - 2)
↓いずれもコンパイル結果は全く同じです:
var fib;fib = function(n) { if (n < 2) { return n; } else { return fib(n - 1) + fib(n - 2); }};
" " でくくった文字列の中では #{変数名} で変数の値を埋め込むことができます。
CoffeeScript:
name = "Nao"console.log "My name is #{name}!"
↓コンパイル結果
var name;name = "Nao";console.log("My name is " + name + "!");
JavaScriptの==
は自動的に型変換が行われるため、意図しない結果となる場合があります。CoffeeScriptでは==
またはis
と書くとJavaScriptの===
に変換されるため、型も含めた厳密な比較となります。
他にもand
,or
などを使って読みやすく書くことができます。対応は以下の通りです。
CoffeeScript | JavaScript-------------+----------- is | === isnt | !== not | ! and | && or | ||
Objectを作るときは以下の書式が使えます。JavaScriptの{ }
も使えます。
またthis.x
の代わりに@x
と書くことができます。
CoffeeScript:
pos = x: 100 y: 200 dump: -> console.log "x:#{@x}, y:#{@y}"
↓コンパイル結果
var pos;pos = { x: 100, y: 200, dump: function() { return console.log("x:" + this.x + ", y:" + this.y); }};
1行でも書けます。
CoffeeScript:
size = width: 100, height:100
↓コンパイル結果:
var size;size = { width: 100, height: 100};
この書式は関数の引数にも応用できます。
CoffeeScript:
myFunc(width:100, height:100)
↓コンパイル結果:
myFunc({ width: 100, height: 100});
for .. in
でループが書けます。
arr = ["a", "b", "c", "d", "e"]for val in arr console.log val
ループ中に配列のインデックスを拾いたい場合はfor
とin
の間に2つ変数を置きます。
arr = ["a", "b", "c", "d", "e"]for val, i in arr console.log "#{i}: #{val}"
of
を使って配列と同じように反復処理ができます。
data = x: 100 y: 200for name, value of data console.log "#{name}: #{value}"
変数名や関数名の直後に?
を付けると「その変数あるいは関数が定義されておりnull以外の値が入っているかどうか」をテストできます。
CoffeeScript:
if myName? console.log "yes"else console.log "no"
↓コンパイル結果:
if (typeof myName != "undefined" && myName !== null) { console.log("yes");} else { console.log("no");}
また.
や()
の前に?
を置くことで、変数や関数が存在する場合のみ処理を進めるようにできます。
CoffeeScript:
console?.log?("Hello World")
↓コンパイル結果:
if (typeof console != "undefined" && console !== null) { if (typeof console.log == "function") { console.log("Hello World"); }}
'''
でヒアドキュメントが使えます。行頭からのインデント分は自動的に削られます。
html = ''' <html> <head> <title>CoffeeScript</title> </head> <body> <table> <tr> <td></td> </tr> </table> </body> </html> '''
↓コンパイル結果
var html;html = '<html>\n<head>\n <title>CoffeeScript</title>\n</head>\n<body>\n <table>\n <tr>\n <td></td>\n </tr>\n </table>\n</body>\n</html>';
関数定義時に->
の代わりに=>
を使うと、this
がバインドされて外側のthis
を参照するようになります。
CoffeeScript:
pos = x: 100 y: 200 dump: -> # 関数内部で@x(this.x)を使いたいのでfuncの定義は=>にしないといけない func = => console.log "x:#{@x}, y:#{@y}" func()
↓コンパイル結果:
var pos;var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };pos = { x: 100, y: 200, dump: function() { var func; func = __bind(function() { return console.log("x:" + this.x + ", y:" + this.y); }, this); return func(); }};
以下のようにしてクラスを使うことができます。
class Animal # newした時に呼ばれるコンストラクタ constructor: (name) -> @name = name say: (word) -> console.log "#{@name} said: #{word}"class Dog extends Animal constructor: (name) -> # 親クラスのコンストラクタを呼ぶ super name say: (word) -> # 親クラスのメソッドを呼ぶ super "Bowwow, #{word}"dog = new Dog("Bob")dog.say("Hello!")>> Bob said: Bowwow, Hello! と出力される
クラスの静的なプロパティを定義するには変数名の頭に@を付けます。
CoffeeScript:
class Dog @TYPE_CHIHUAHUA = 1
↓コンパイル結果:
var Dog;Dog = (function() { function Dog() {} Dog.TYPE_CHIHUAHUA = 1; return Dog;})();
setTimeoutなど引数に無名関数を渡したい場合は、次の行頭にカンマを付けます。
CoffeeScript:
setTimeout -> console.log("ok"), 1000
コンパイル後の#"ok");}, 1000);
(2011/4/22 追記)1行で書きたい場合は以下のようにします。
setTimeout (-> console.log("ok")), 1000
name
という変数がある場合、{name}
と書くと{name:name}
の意味になります。
また=
の左辺に{var}
を書く記法があり、require()
の際に便利です。
例えば便利なライブラリの一つにUnderscore.jsがありますが、これを使う場合、CoffeeScriptのファイルとunderscore.jsが同じディレクトリにあれば以下のようにしてロードできます。
{_} = require './underscore'
これは以下のコードと同等です。
_ = require('./underscore')._
なおUnderscore.js 1.1.3以降では._
を付ける必要がなくなったため、以下のように読み込むことができます。
_ = require './underscore'
ほかにも便利な構文がいろいろあります。詳細はCoffeeScriptのサイトを参照してください。
実際に自分で使っていて気づいたtipsを紹介します。
?
の使い分け?
記号は三項演算子ではないため、意図したコードになっているか注意してください。
変数名や関数名の直後に?
を置いた場合は存在チェックになります。
一方、変数の後にスペースを入れて?を置くと、変数が未定義かnullの場合に特定の値を代入する式が書けます。
CoffeeScript:
root = exports ? window
↓コンパイル結果:
var root;root = typeof exports != "undefined" && exports !== null ? exports : window;
?=
を使って、変数が未定義またはnullの場合だけ代入することもできます。
CoffeeScript:
console ?= {}
↓コンパイル結果:
typeof console != "undefined" && console !== null ? console : console = {};
従来の三項演算子の用途には?
は使えないため、代わりにif/then/else
を使います。
result = if isTrue? then "true" else "false"
CoffeeScript:
if opts.key? console.log "yes"
と書いた際、opts.keyの存在はチェックされますが、opts変数の存在自体はチェックされません。もしoptsがundefinedやnullなら実行時にエラーが出てしまいます。
↓コンパイル結果:
if (opts.key != null) { console.log("yes");}
これに対処してoptsの存在もチェックするには以下のように書きます。
CoffeeScript:
if opts?.key? console.log "yes"
↓コンパイル結果:
if ((typeof opts != "undefined" && opts !== null ? opts.key : void 0) != null) { console.log("yes");}
for文の値を以下のように受け取ると、配列のmapに相当する処理が簡潔に書けます。
CoffeeScript:
values = (num * 2 for num in [1..5])console.log values>> [ 2, 4, 6, 8, 10 ] と出力される
↓ コンパイル結果:
var num, values;values = (function() { var _results; _results = []; for (num = 1; num <= 5; num++) { _results.push(num * 2); } return _results;})();console.log(values);
関数では最後の値が自動的にreturnされますが、returnを書かずにforを関数の最後に置いた場合、配列を作ってreturnしようとしてしまうので気をつけてください。
例えば以下のようなケースです。
CoffeeScript:
myFunc = -> for i in [0..10] something()
↓コンパイル結果:
var myFunc;myFunc = function() { var i, _results; _results = []; for (i = 0; i <= 10; i++) { _results.push(something()); } return _results;};
_results
が生成されてreturnされています。
これを防ぐには明示的にreturnを書きます。
CoffeeScript:
myFunc = -> for i in [0..10] something() return
↓compile結果:
var myFunc;myFunc = function() { var i; for (i = 0; i <= 10; i++) { something(); }};
is
を使うときの型に注意JavaScriptの==
では自動的に型変換が行われますが、is
では型もチェックされますので注意してください。
例:
if 4 is "4" console.log trueelse console.log false>> false が出力されるif `4 == "4"` # ``で囲うと直接JavaScriptを書けます。 console.log trueelse console.log false>> true が出力される
-w
オプションでのコンパイルエラー-w
オプションを付けてcoffeeコマンドを起動しているとき、ソースを修正したつもりでもコンパイルエラーになっていてjsファイルが更新されていない時がありますので気をつけましょう。
MacであればコンパイルエラーをGrowlで通知してくれるJitterというツールもあります。
実はコンパイルをしなくても、ライブラリを読み込むことでブラウザ上でCoffeeScriptのコードを実行できます。
以下のようにjsdo.itでもCoffeeScriptが使えます。ぜひ Fork ボタンを押していじってみてください!
CoffeeScript出来るかな - jsdo.it - share JavaScript, HTML5 and CSS
ただしオーバーヘッドなども出てきますので、本番環境ではJavaScriptにコンパイルしてデプロイするようにしてください。
以上、長くなりましたがCoffeeScriptについて紹介させていただきました。
カヤックでは新しい技術に興味のあるあなたのようなエンジニアを求めています!
CoffeeScript (http://jashkenas.github.com/coffee-script/)
引用をストックしました
引用するにはまずログインしてください
引用をストックできませんでした。再度お試しください
限定公開記事のため引用できません。