書籍転載:Ruby on Rails 4アプリケーションプログラミング
CoffeeScript入門(前編) ― CoffeeScriptの基本構文
Rubyプログラマーの必須知識「CoffeeScript」の基礎として、基本構文/変数とリテラル表現/演算子/制御構文を解説。書籍転載の8本目(「Part 3《応用編》 第9章 クライアントサイド開発」より)。
書籍転載について
ご注意
本記事は、書籍の内容を改変することなく、そのまま転載したものです。このため用字用語の統一ルールなどはBuild Insiderのそれとは一致しません。あらかじめご了承ください。
前回までは、Railsアプリケーションの作成方法について解説しました。今回からは応用編としてCoffeeScriptについて説明します。
■
9.3 CoffeeScript
CoffeeScriptとは、一言で言ってしまうと、JavaScriptのコードを生成するためのコンパクトな高級言語です。CoffeeScriptを利用することで、JavaScriptでは冗長なコードを記述せざるを得なかった、また、誤りが混在しやすかった局面で、よりシンプルで、安全なコードを記述しやすくなります。
CoffeeScriptのコードはJavaScriptにコンパイルされた上で実行されますので、クライアント環境に特別なプラグインやライブラリは必要ありません。文法もJavaScriptをベースとしつつ、Rubyのそれにもよく似ていますので、これまでRuby+JavaScriptを利用してきた人であれば、習得は容易でしょう。
以下では、CoffeeScriptの基本的な構文ルールを解説します。JavaScript( 一部、jQuery) の基本を理解していることを前提に、CoffeeScript固有の規則をまとめますので、JavaScriptの基礎的な事項については、拙著「JavaScript本格入門」(技術評論社)、「10日でおぼえる jQuery入門教室」(翔泳社)などの専門書を参照してください。
9.3.1 CoffeeScriptの基本
RailsではAsset Pipelineを利用して、CoffeeScriptを自動的にコンパイルしてくれますので、特にコンパイル作業を意識する必要はありません。拡張子を「.js.coffee」とし、/app/assets/javascriptsフォルダに配置するだけで利用できます。
もっとも、CoffeeScriptに慣れないうちはRails上でいきなり動かすよりは、コンパイル/実行結果を確認しながらコーディングできた方が便利です。以下に手元でCoffeeScriptコードをコンパイルするための方法を示します。
1本家サイトの簡易インタプリタを利用
まずは、CoffeeScript本家サイトで提供されている簡易インタプリタを利用させてもらうのが手っ取り早いでしょう。このインタプリタはブラウザ上で動作しますので、特別な準備は必要ないのが特長です。
インタプリタは、本家サイトの上部メニューから[TRY COFFEESCRIPT]をクリックすることで起動できます。ウィンドウの左枠にCoffeeScriptのコードを入力すると、右枠にリアルタイムでコンパイル済みのJavaScriptコードが表示されます。また、右上の[Run]ボタンをクリックすることで、コードを実行させることも可能です。
2オフライン環境でコンパイルする
1.2節で既にNode.jsをインストールしているならば、CoffeeScriptのコンパイル環境を準備するのは簡単です。コマンドプロンプトからnpmコマンド*12を実行してください。
> npm install -g coffee-script
|
- *12 Node Package Manager。Node.jsで作成されたパッケージを管理するためのツールです。
インストールに成功したら、あとは以下のコマンドで.js.coffeeファイルをコンパイルできます。hello.js.coffeeと同じフォルダに、コンパイル結果としてhello.js.jsが生成されていれば成功です。
> coffee -c hello.js.coffee ……コンパイル(hello.js.jsを生成)
|
本節のサンプルは、インタプリタ上で動作を確認しています。また、インタプリタが出力するJavaScriptのコードも併記しますので、理解の手助けとしてください。
9.3.2 CoffeeScriptの基本構文
まずは、CoffeeScriptを利用する上で最初におさえておきたいポイントを、ごく簡単なサンプルで確認します。文法レベルでのJavaScriptとの違いをおさえておきましょう。
4
1
4
3
|
###
CoffeeScriptの基本文法
###
i = 10
# 条件分岐構文
if i is 1
window.alert '変数iは1' 2
|
/*
CoffeeScriptの基本文法
*/
var i;
i = 10;
……単一行コメントは削除される
if (i === 1) {
window.alert('変数iは1');
}
|
ポイントは、以下の4点です。
1文末のセミコロンは不要
JavaScriptと違って、文末のセミコロン(;)は不要です。ただし、1行で複数の文をまとめる場合、セミコロンは省略できません。たとえば、以下のようにです。
window.alert 'one'; window.alert 'two'
|
逆に、文が継続していることが明らかな文脈では、文の途中で改行しても構いません。よって、以下のようなコードは正しいCoffeeScriptのコードです。
window.
alert 'one'
|
ただし、無制限な改行は思わぬバグの原因となりますので、要注意です。たとえば以下のコードは(おそらく)一文であることを意図したコードですが、CofeeScriptでは2文と見なされます。
return
height * width / 2
|
文の途中で改行を加える際には、演算子やカンマ、左カッコの直後など、限られた文脈に限定するのが無難です。
2関数/メソッドの引数を表すカッコは不要
関数/メソッドの引数はカッコで括らなくても構いません。ただし、以下のようなケースには要注意です。
hoge foobar 2, 3
|
上記は、「hoge(foobar(2), 3)」であることを意図したコードですが、曖昧です。結果、CoffeeScriptでは「hoge(foobar(2, 3))」のように、すべての引数がfoobar関数に吸収されてしまうのです。
引数の対応関係が不明確な場合(関数が入れ子の関係にある場合など)には、カッコで括っておくのが無難です。
3if、switch、whileなどのブロックはインデントで表す
あとから触れるように、CoffeeScriptでも、JavaScriptと同じくif、switch、whileなどの制御構文を利用できます。しかし、配下のブロックは{…}で括る代わりに、インデントで表現しなければならない点に注意してください。
これは、単なる文法の違いにも思えるかもしれませんが、
「見た目の階層」と「論理的な構造」とを文法レベルで一致させる
という意味で、とても重要な性質です*13。
- *13 コードの見た目の階層が、そのまま論理的なブロックを表しているため、コードの可読性が格段に改善します。一方、JavaScriptではインデントは単なる空白であり、論理的なブロックと一致しているべきですが、一致していなくても誤りではありません。
4コメントは #、### … ### の2種類
CoffeeScriptのコメントには、#(単一行コメント)、### … ###(複数行コメント)の2種類があります。ただし、単一行コメントはコンパイル時に破棄されますが、複数行コメントはコンパイルの前後でも維持されるという点に注意してください。その性質上、複数行コメントにはライセンス表示など、配布時にも維持するような内容を記述します。
9.3.3 変数とリテラル表現
ここからは、CoffeeScriptの個別の構文について解説していきます。まずは、変数と、リテラル表現についてです。なお、数値リテラル、正規表現リテラルなどについては、JavaScriptのそれに準じますので、ここでは割愛します*14。
- *14 以降も、原則としてJavaScriptと異なる点にフォーカスして解説を進めます。言及していない構文は、JavaScriptのそれに準じると考えてください。
変数
変数の宣言に、varキーワードは不要です。変数のスコープも最初に値が代入された位置で決まります。
title = 'Rails4'
|
var title;
title = 'Rails4';
|
その性質上、CoffeeScriptでは、関数の内外で同名の変数があった場合、いずれもグローバル変数であると見なされます。グローバル変数とローカル変数とは、名前で明確に区別するようにしてください。
i = 100
hoge = ->
i = 100
return
|
var hoge, i;
i = 100;
hoge = function() {
i = 100; 同じグローバル変数
};
|
文字列リテラル
文字列リテラルは、JavaScriptと同じくダブルクォート(")、またはシングルクォート(')で括ります。ダブルクォートで囲んだ文字列には、#{変数名}の形式で変数や式を埋め込むこともできます(変数展開)。
title = 'Rails4'
msg = "Let's Try, #{title}!"
alert(msg)
|
var msg, title;
title = 'Rails4';
msg = "Let's Try, " + title + "!";
alert(msg);
|
更にCoffeeScriptは、複数行の文字列を表現するための、ヒアドキュメントという構文にも対応しています。ヒアドキュメントは「"""…"""」(変数展開あり)、または「'''…'''」(変数展開なし)で表します。
body = """
みなさん、こんにちは。
Railsに挑戦。
"""
↑もっとも少ないインデントを基準に、空白をサプレス
|
var body;
body = "みなさん、こんにちは。\n Railsに挑戦。";
|
ヒアドキュメントに対応していないJavaScriptでは、文字列リテラルに直接の改行を含めることはできませんので、代わりに「\n」で改行を表現しなければなりません。
なお、ヒアドキュメントの中のインデントは、コンパイル時にもっとも少ないものを基準に除去されます。よって、コードの読みやすさを考慮して、上の例のようにインデントを加えても、文字列に余計な空白が混入することはありません。
真偽リテラル
JavaScriptのtrue、falseを利用することもできますが、CoffeeScriptでは、更にエイリアスとして、
- on、off
- yes、no
のようなリテラルも用意されています。意味は同じですが、文脈によって使い分けることで、コードをより読みやすく表すこともできるでしょう。
配列/ハッシュリテラル
配列やハッシュ(オブジェクト)のリテラルは、JavaScriptと同じく[…]、{…}で表現できます。ただし、以下の点がJavaScriptとは異なります。
- 要素の区切り文字としてカンマ(,)だけでなく改行も利用できる
- 最後の要素をカンマで終わっても構わない*15
- *15 正確には最後のカンマはJavaScriptでも利用できますが、ブラウザによって動作が異なるため、利用すべきではありません。
具体的な例も見てみましょう。
# 最後にカンマを付けても構わない
ary1 = [ 100, 200, 300, ]
hash1 = { a:100, b:200, c:300, }
# 要素を(カンマではなく)改行で区切っても良い
ary2 = [
'Red'
'Yellow'
'Green'
]
hash2 = {
a: 'Red'
b: 'Yellow'
c: 'Green'
}
|
var ary1, ary2, hash1, hash2;
ary1 = [100, 200, 300];
hash1 = { a: 100, b: 200, c: 300};
ary2 = ['Red', 'Yellow', 'Green'];
hash2 = { a: 'Red', b: 'Yellow', c: 'Green'};
|
インデントで階層付けすることで、入れ子のハッシュも表せます。この記法はYAMLと同じ記法なので、Railsで触れてきた人にとっては直感的にも理解しやすいですね。
picture =
Landscape:
title: '雪と梅'
price: 10000
delete: false
Portrait:
title: '傘さす子ども'
price: 25000
delete: false
|
var picture;
picture = {
Landscape: {
title: '雪と梅',
price: 10000,
"delete": false ……予約語は自動的にクォートで処理される
},
Portrait: {
title: '傘さす子ども',
price: 25000,
"delete": false
}
};
|
コンパイル済みのJavaScriptコード(太字部分)を確認すると、JavaScriptの予約語(サンプルではdelete)が自動的にクォート処理されています。すべての予約語を記憶するのは難しいことですが、こうした点もCoffeeScriptならば、あらかじめフォローしてくれるのです。
9.3.4 演算子
演算子は、ほぼJavaScriptと同じものを利用できますが、一部のものについてはエイリアス(別名)が用意されています。
CoffeeScript | JavaScript | 利用例 |
---|---|---|
is | === | a is b |
isnt | !== | a isnt b |
not | ! | not flag |
and | && | a is 1 and b is 2 |
or | || | a is 1 or b is 2 |
また、CofeeScript特有の規則もありますので、以下にまとめておきます。
「==」演算子は利用できない
JavaScriptの「==」演算子は、比較に際してオペランド(被演算子)のデータ型を変換して「なんとか等しいと見なせないか」と、あれこれ努力してくれる親切な演算子です。これによって、開発者はさほどデータ型を意識することなくコーディングできるわけです。
ただ、この親切は時として「余計なお世話」になる場合があります。たとえば、
'0X10' == 16
|
は「0X」が16進数と解釈された結果、trueとなります。しかし、このような型変換を期待して、
'010' == 8
|
のような式を表すと、こちらはfalseとなります。8進数を表す接頭辞「0」が、正しく解釈されないのです。
以上のような例はほんの一例で、「==」演算子はそのお節介さから意図せぬ挙動をとることもしばしばで、原則として利用すべきではありません。CoffeeScriptでは、このルールが更に徹底されており、「==」演算子は無条件に「===」演算子(厳密な比較)に変換されます。利用すべきでない「==」演算子は、CoffeeScriptにはそもそも存在しないのです。
x == 1
|
x === 1;
|
連結した比較演算子で範囲条件を表現できる
たとえば、50~100の範囲を表現するために、「x <= 50 && x > 100」のような条件式を表すことはよくあります。範囲であることをより明確にするために、「50 <= x && x < 100」のように表す場合もあるかもしれません。
CoffeeScriptでは、このようなケースで「50 <= x < 100」のような表現を認めていますので、より直感的に範囲を表せます。
変数の存在をチェックする「?」演算子
CoffeeScript独自の演算子として、「?」演算子(存在演算子)もあります。
変数の後方に「title?」のように存在演算子を付与することで、変数の有無を確認できます。なお、ここでの「存在」とは、変数が定義済みであり、なおかつ、null以外の値が設定されている状態のことを言います。
# 変数titleが存在するかを判定
if title?
alert(title)
|
if (typeof title !== "undefined" && title !== null) {
alert(title);
}
|
コンパイル結果を見てもわかるように、JavaScriptではnullであることを確認する前に、titleがundefined(未定義)でないことをチェックしなければなりません。さもないと、「'title' は定義されていません」のようなエラーが発生するためです。しかし、CoffeeScriptでは、このような冗長な記述を「?」一文字で表現できるのです。
その他にも、存在演算子は、以下のような用途で利用できます。
1デフォルト値の設定
変数が存在しない場合に、デフォルト値を設定するために利用できます。
value = null
# 変数valueがnullの場合は、デフォルト値wingsをセット*16
value ?= "wings"
# 変数valueが存在しなければ、変数initialには0をセット
initial = value ? 0
|
var initial, value;
value = null;
if (value == null) {
value = "wings";
}
initial = value != null ? value : 0;
|
- *16 ただし、この用途では最初に変数(ここではvalue)を宣言しておく必要があります。さもないと、未定義エラーが発生してしまうためです。
ただし、変数valueが存在しない、もしくは0、空文字列の場合に、デフォルト値をセットしたいならば、「||=」を利用しなければなりません。
value ||= "hoge"
|
2関数やプロパティ/メソッドの安全な呼び出し
関数やメソッドを呼び出す場合にも、存在演算子は有効です。以下はそれぞれ、
- 関数funcが存在する場合のみ実行
- オブジェクトobjが存在する場合のみfooプロパティにアクセス
する例です。
# 関数funcが存在する場合のみ実行
alert(func?())
# オブジェクトobjが存在する場合のみfooプロパティにアクセス
alert(obj?.foo)
|
alert(typeof func === "function" ? func() : void 0);
alert(typeof obj !== "undefined" && obj !== null ? obj.foo : void 0);
|
コンパイル結果を見てもわかるように、関数ではtypeof演算子の戻り値がfunctionである(=関数型である)ことだけをチェックしています。関数の場合は、そもそもnull/undefinedでないかではなく、対象が関数であることが呼び出しの条件となるからです。
三項演算子は利用できない
「?」が存在演算子として認識される結果、CoffeeScriptでは三項演算子を利用することはできません。
x = (flag ? 1 : 0)
|
var x;
x = typeof flag !== "undefined" && flag !== null ? flag : {
1: 0
};
|
コンパイル結果を見てもわかるように、存在確認に置き換わってしまっているのです。もしもCoffeeScriptで三項演算子「的」な表現をしたい場合には、以下のようにif命令を利用してください(if命令についてはあとで改めます)。
x = if flag then 1 else 0
|
var x;
x = flag ? 1 : 0;
|
連続する数値を表す範囲演算子
範囲演算子「..」を利用することで、m~nの範囲の数値配列をスマートに生成できます。たとえば以下は、1~10の値を表現した例です。JavaScriptであれば、すべての値をハードコーディングするか、forループで動的に生成する必要があるのが、範囲演算子を利用することで、ぐんとシンプルに表現できます。
x = [10..20]
|
var x;
x = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20];
|
数値範囲の種類によって、配列生成の方法も変化します。以下は1~100の値を生成する例です。forループで配列_resultを動的に生成していることが見て取れます。
x = [1..50]
|
var x, _i, _results;
x = (function() {
_results = [];
for (_i = 1; _i <= 50; _i++){ _results.push(_i); }
return _results;
}).apply(this);
|
なお、「...」演算子(ピリオドが3個)を利用した場合には、範囲から終点の数値が除かれます。
x = [10...20]
|
var x;
x = [10, 11, 12, 13, 14, 15, 16, 17, 18, 19];
|
範囲演算子を利用した例については、for…in命令(後述)の項も合わせて参照してください。
9.3.5 制御構文
CoffeeScriptでも、if、switch、for、whileなど主な制御構文を利用できます。ただし、微妙に構文が変化していたり、unless、for…inなどCoffeeScript固有の命令もあります。似ているだけに、両者の違いを意識しながら理解を進めてください。
汎用的な条件分岐を表す - if…else命令
本節冒頭でも述べたように、制御命令のブロック{…}ブロックはインデントで表現します。1行で表現するならば、条件式と実行文の間をthenキーワードで区切っても構いません。以下のコードは、いずれも同じ意味です。
# ifブロックをインデントで表現する標準形式
if x is 1
alert 'success'
else
alert 'failure'
# thenキーワードを利用することで、1行でも表せる
if x is 1 then alert 'success' else alert 'failure'
|
// いずれも以下のif命令にコンパイルされる
if (x === 1) {
alert('success');
} else {
alert('failure');
}
|
対象の処理が1文であれば、if句を命令の後方に記述する後置構文も利用できます。
alert 'success' if x is 1
|
if (x === 1) {
alert('success');
}
|
前置/後置いずれもそれほどコード量には変化がないと思われるかもしれません。
しかし、コードの読みやすさに差があります。
従来の構文では「条件式を●○であれば■□をしなさい」と、条件式が表現の主となります。しかし、後置構文では「~しなさい。ただし、条件式が正しければね!」と、処理そのものが主となります(条件式はあくまで付随的な情報というわけですね)。いずれの構文も機能そのものは同じですが、コードを読んで、いずれがよりスムーズに把握できるかという観点で使い分けると良いでしょう。
後置構文は、後述するunless/while/until命令でも利用可能です。
否定の条件分岐を表現する - unless命令
条件式がtrueの場合に命令を実行するif命令に対して、条件式がfalseの場合に命令を実行するunless命令も用意されています。CoffeeScript固有の構文です。
記述自体がシンプルになるというわけではありませんが、「否定」というのは人間の頭にとってどうしても混乱のもとになりやすいものです。条件式から否定を取り除き、できるだけ人間の思考に近い形で表現できることで、結果、論理的なバグを減らせます。
unless x is 1
alert 'failure'
|
if (x !== 1) { ……否定の条件式に変換される
alert('failure');
}
|
unless命令でもelse句は利用できますが、否定の否定はコードを読みにくくする原因ですので、原則として利用すべきではありません*17。
- *17 単純に、if…else命令で代替できます。
等価比較による多岐分岐を表現する - switch…when…else命令
式の値に応じて処理を分岐するのが、switch命令の役割です。JavaScriptにもswitch命令はありますが、CoffeeScriptでは以下の点が異なります。
- case句の代わりにwhen句、default句の代わりにelse句を利用する
- when句にはカンマ区切りで複数の値を指定できる
- 条件句を明示的に抜けるbreak命令は不要
以下に、具体的な例を示します。
switch point
when 3
alert '上級'
when 2, 1 ……複数の値を列挙できる
alert '中級'
else
alert '初級'
|
switch (point) {
case 3:
alert('上級');
break; ……breakが自動的に補われる
case 2:
case 1:
alert('中級');
break;
default:
alert('初級');
}
|
特にbreak命令の書き忘れによる不具合は、JavaScriptではよくある誤りです。CoffeeScriptでは、そもそもbreakを必要としないので、潜在的なバグの可能性を防げます。
条件式によって処理を繰り返す - while/until命令
条件式がtrueの間処理を繰り返すにはwhile命令、条件式がfalseの間(trueになるまで)処理を繰り返すにはuntil命令を利用します。until命令は、CoffeeScript固有の命令です。unless命令でも触れたように、条件式に否定が含まれる場合は、until命令を利用することで、コードがより直感的にわかりやすくなります。
# 変数iが10未満の間、処理を繰り返すCoffee
while i < 10
alert i
i++
# iが10より大きくなるまで、処理を繰り返す
until i > 10
alert i
i++
|
while (i < 10) {
alert(i);
i++;
}
while (!(i > 10)) { ……条件式を反転した上でwhile命令に変換
alert(i);
i++;
}
|
while/untilいずれの命令でも、後置構文(前述)は利用可能です。また、ループを抜けるためのbreak/continue命令は、JavaScriptそのままに利用できます。
配列をもとに特定の処理を繰り返す - for…in/for…of命令
配列から順に値を取り出すならば、for…in命令を利用します。たとえば以下は、配列aryから取り出した要素を、順にダイアログに表示する例です。for…inブロックの中では、仮変数(以下の例ではcolor)を介して、配列要素にアクセスできます。
ary = [ 'Red', 'Yellow', 'Green' ]
for color in ary
alert color
|
var ary, color, _i, _len;
ary = ['Red', 'Yellow', 'Green'];
for (_i = 0, _len = ary.length; _i < _len; _i++) {
color = ary[_i];
alert(color);
}
|
ハッシュで同じような操作を行うならば、for…of命令を利用します。for…of命令の仮引数(以下の例ではkey、value)には、ハッシュのキー/値がセットされます。
hash = { name:'Uta', age:1, kind:'hamster' }
for key, value of hash
alert "#{key}=#{value}"
|
var hash, key, value;
hash = {
name: 'Uta',
age: 1,
kind: 'hamster'
};
for (key in hash) {
value = hash[key];
alert("" + key + "=" + value);
}
|
指定された回数だけ処理を繰り返す - for…in命令(2)
CoffeeScriptでは、JavaScriptのforにあたる命令はありません。for…in命令と範囲演算子で代用してください。
for i in [5..10]
alert i
|
var i, _i;
for (i = _i = 5; _i <= 10; i = ++_i) {
alert(i);
}
|
もしも終了条件「i < 10」(イコールなし)を表現したいならば、太字の部分を[5...10](ドット3個)で表します。
■
次回は、関数/オブジェクト指向構文/即時関数について説明します。
※以下では、本稿の前後を合わせて5回分(第6回~第10回)のみ表示しています。
連載の全タイトルを参照するには、[この記事の連載 INDEX]を参照してください。
8. 【現在、表示中】≫ CoffeeScript入門(前編) ― CoffeeScriptの基本構文
Rubyプログラマーの必須知識「CoffeeScript」の基礎として、基本構文/変数とリテラル表現/演算子/制御構文を解説。書籍転載の8本目(「Part 3《応用編》 第9章 クライアントサイド開発」より)。
9. CoffeeScript入門(後編) ― 関数/オブジェクト指向構文/即時関数
CoffeeScriptの基礎を解説。今回は関数/オブジェクト指向構文/即時関数について説明する(書籍転載の9本目)。CoffeeScriptをマスターしよう。
10. Sass(SCSS)入門
CSSのコードを生成するための言語「Sass(SCSS)」の基礎として、基本的な使い方/スタイル定義のネスト/変数/演算子/関数/ディレクティブ/について解説。書籍転載の10本目(「Part 3《応用編》 第9章 クライアントサイド開発」より)。