CoffeeScriptの文法メモ

ソースコードにある'#=>'は説明用のもので、その値を評価した値を載せている。CoffeeScriptの機能ではない。
確認したversionは1.1.0

基本

ブロックはインデントで表現

hello = (name) ->
  console.log "hello #{name}"

hello 'kei_q'

変数の宣言に'var'は必要無い

n = 1

comment

コメントは'#'で書く

n = 1 # comment

'###'で範囲コメント
コメント内で'*/'は使えない

###
comment
###

string

文字列の途中で改行を入れても無視される

s = "hoge
fuga"
s #=> "hogefuga"

文字列の中に'#{}'使うことで、式を評価した値を埋め込むことができる。シングルクォートでは展開されない。

s = 'hoge'
"#{s}" #=> "hoge"
'#{s}' #=> '#{s}'

クォート、もしくはダブルクォート3つでheredocumentとなる
このとき、先頭の空白は行頭の空白が最も少ない行の分だけ省かれる

s = '''
  first line
    second line
'''
s #=> 'first line\n  second line'

s2 = '''
  first line
 second line
'''
s2 #=> ' first line\nsecond line'

bool

trueとfalseにはそれぞれ別名が用意されている。

bool 別名
true yes, on
false no, off

regex

複数行にわたるregexが書ける
このregexでは空白が無視される
また、#{}による式の展開もできる

r = /// (
  hoge    # comment
  | fuga  # comment
  | #{s}  # comment
)
///

array

数値の範囲による配列の生成ができる。最後の値を含む場合は'..', 含まない場合は'...'を使用する。

[1..3]  #=> [1, 2, 3]
[1...3] #=> [1, 2]
[3..1]  #=> [3, 2, 1]

配列に対して範囲を指定すると、該当する部分を抜き出すことができる。範囲は0から数える。
後ろから数える場合、負の値で指定する。こちらは-1から数える。
始点か終点の片方を省略することができる。その場合、範囲は始点なら0、終点なら-1を指定したものと同じになる。

arr = [0..9]
arr[4..5] #=> [4, 5]
arr[4..]  #=> [4, 5, 6, 7, 8, 9]
arr[..5]  #=> [0, 1, 2, 3, 4, 5]
arr[..-2] #=> [0, 1, 2, 3, 4, 5, 6, 7, 8]

sliceは左辺にも使用することができる。
詳しくは後述のパターンマッチを参照

arr = [0..9]
arr[4..5] = [-1, -1]
arr #=> [0, 1, 2, 3, -1, -1, 6, 7, 8, 9]

'in'演算子を使うことで、配列に値が含まれているか調べることができる。

3 in [1, 2, 3] #=> true

object

オブジェクトリテラルでは、'{'を省略したyamlのような書き方ができる。
以下の二つのobjは同じ。

obj =
  a : 1
  b :
    c : 3
    d : 4

obj = {
  a:1,
  b:{
    c:3,
    d:4
  }
}

classなどの予約語をオブジェクトのプロパティとして使用する場合、明示的に文字列にしなくてもそのまま使える。

obj =
  class : 'hoge'
  var : 'fuga'

obj.class #=> 'hoge'

'of'でkeyが存在するか調べることができる

obj = { a : 1, b: 2}
'a' of obj #=> true

control expression

全般

javascriptの制御構文は使えない
値を返す

if, unless

条件式の括弧は必要無い

if str == 'hoge'
  f()
else if str == 'fuga'
  g()
else
  h()

unless(= if not)が使用できる

unless str == 'hoge'
  f()

後置if, unlessが使用できる

f() if str == 'hoge'
g() unless str == 'hoge'

三項演算子はない。if-then-elseを使用する

if true then f() else g()

値を返すので、変数に代入できる

s = if false
  'a'
else if true
  'b'
else
  'c'

s #=> 'b'
switch

breakは自動挿入される
whenの項目には複数記述できる
whenに続けて処理を書く場合は'then'の後に書く

switch str
  when 'foo' then h()
  when 'hoge, 'fuga'
    f()
  else
    g()

switchの条件省略をすると!!hogeでtrueになるものが選択される

s = switch
  when undefined
    'hoge'
  when true
    'fuga'

s #=> 'fuga'
while, until, loop

条件式において括弧は必要無い

i = 0
while i < 9
  i += 1

until(=while not), loop(while true)が用意されている

i = 0
until i > 10
  i += 1

loop
  if bool
    break
  f()

後置while, untilが使用できる

f() while bool
g() until bool

whenで条件が記述できる

while i++ when arr[i]
  f(arr[i])
for-in

配列のループにはfor-inを使用する

for val in array
  hoge(val)

引数二つで配列のindexも合わせてbindされる

for val, index in array
  f(index, val)
for-of

オブジェクトのkeyを走査するにはfor-ofを使用する

for key of obj
  key

引数二つでkeyとその値を返す

for key, val of obj
  f()

forの後に'own'を付けると、hasOwnPropertyでチェックした値だけになる

for own key, val of obj
  f()
for-in, for-of共通の記述

'when'で条件を記述し、trueになったもののみblockを評価

evenlist = for n in [1..9] when even(n)
  n

evenlist #=> [2, 4, 6, 8]

'by'でn個毎に評価。最初の値は評価される

ns = for n in [1..9] by 3
  n

ns #=> [1, 4, 7]

後置for

f(val) for val in array

comprehensions
括弧を付けないと異なる解釈をされるので注意

arr = (n*n for n in [1..3]) #=> [1, 4, 9]

括弧を省略すると、以下のような意味になる

v[i] = val[i] for val, i in array
(v[i] = val[i]) for val, i in array

forの次にdoが付いた関数を書くと、forの中に直接処理が書かれるのではなく、別に関数を定義して呼び出したのと同じになる
#788を参照

for n in [1..9]
  do (n) ->
    console.log

_fn = (n) ->
  console.log
_fn(n) for n in [1..9]
try catch
try
  1 / 0
catch err
  "error is #{err}."
finally
  f()

関数

定義

宣言
関数の定義は、引数のリスト、'->'、関数本体の順に書く
関数の返り値は関数の最後の式

add = (x, y) -> x + y
add(1, 2) #=> 3

無引数なら引数の括弧は省略できる

hello = -> console.log 'hello'

関数の引数に値を書いておくことでdefault引数が使用できる
該当する引数が無い場合、指定した値が入る

add = (x, y = 1) -> x + y
add(1) #=> 2
add(1, 2) #=> 3

可変長引数
splatは引数の中で一つのみ
splatの前後に引数がある場合、それを考慮して、残りの値をsplatに入れる

print = (y...) -> y.join(', ')
print = (x, y..., z) -> "#{x}, #{y}, #{z}"

引数にはECMAScript harmonyのdestructuringが使用できる

f = ([a, b], c) -> a+b+c
f [1, 2], 3

関数の前に'do'を付けると、即時関数となる

do ->
  hoge
呼び出し

splat
呼び出しのとき、'...'を配列の後ろに付けることで、展開して関数を呼び出す

f = (a, b, c) -> a+b+c
arr = [1, 2, 3]
f arr... #=> 6

関数呼び出しでは括弧を省略できる
ただし、引数が無い場合は関数そのものと区別が付かないため、呼び出したいときは明示的に括弧を付ける必要がある

add 1, 2
add(1, 2)
add()

複数呼び出しでの括弧補間ルールは、単に式の右から関数の括弧を補間する

add add 1, 2, add 3, 4
add (add 1, 2, (add 3, 4))

上記ルールにより、以下の例は多くの人が期待したとおりにはならない。method chainでは素直に括弧を付けよう

obj
  .hoge 1
  .fuga 2

obj.hoge(1).fuga(2)   # 期待してたもの
obj.hoge((1).fuga(2)) # 現実

class

基本形
コンストラクタは'constructor'という名前で定義する
extendsには一つのみ指定可能
メソッドはオブジェクトリテラルと同様に':'で記述する
インスタンス変数は'@'で書くことができる。これは'this.'と書いたのと同じ意味になる

class A
  constructor: (v) ->

class B extends A
  constructor: () ->
    @x = 1

  getX: -> @x
  setX: (@x) ->

引数に@でthisに代入
以下は同じ

class A
  constructor: (@v) ->

class A
  constructor: (v) ->
    @v = v

'super'で親クラスのメソッドを呼べる

class A
  f: (n) -> n * 2

class B extends A
  f: (n) ->
    super(n) + 3

(new B).f(1) #=> 5

=>を使用すると、関数を定義した場所のthisを関数の内側でbindする

class A
  constructor: (@x) ->
  f: -> setTimeout (=> console.log @x), 2000

methodに@はthisではなくてクラス名に変換
以下は同じ

class Math
  @PI: 3.1415

Math.PI = 3.1415

extendsは演算子としても定義されており、単独で書くこともできる
右結合

A extends B extends C
A extends (B extends C)

classの名前は省略可能。classの返り値を代入することで、以下のように定義することもできる

exports.Hoge = class
  method: ->

blockも省略可能なので、以下もコンパイル可能

class

演算子

以下の演算子は意味が変更されている
javascriptでの'==', '!='はCoffeeScriptには存在しない

CoffeeScript 意味
== ===
!= !==
1 == '1' #=> false

いくつかの演算子には別名が用意されている
|*alias|*意味|
| is | != |
| isnt | == |
| not | ! |
| and | && |
| or | \|\| |

以下の演算子は繋げて記述できる

  • >
  • <=
  • =>
  • is(==)
  • isnt(!=)
x = 3
1 < x < 10 #=> true

演算子と優先順位をgrammer.coffeeから転載
上ほど優先順位が高い

operators = [
  ['left',      '.', '?.', '::']
  ['left',      'CALL_START', 'CALL_END']
  ['nonassoc',  '++', '--']
  ['left',      '?']
  ['right',     'UNARY']
  ['left',      'MATH']
  ['left',      '+', '-']
  ['left',      'SHIFT']
  ['left',      'RELATION']
  ['left',      'COMPARE']
  ['left',      'LOGIC']
  ['nonassoc',  'INDENT', 'OUTDENT']
  ['right',     '=', ':', 'COMPOUND_ASSIGN', 'RETURN', 'THROW', 'EXTENDS']
  ['right',     'FORIN', 'FOROF', 'BY', 'WHEN']
  ['right',     'IF', 'ELSE', 'FOR', 'DO', 'WHILE', 'UNTIL', 'LOOP', 'SUPER', 'CLASS']
  ['right',     'POST_IF']
]

予約語

以下のwordが予約語として定義されている

case, default, function, var, void, with
const, let, enum, export, import, native
__hasProp, __extends, __slice, __bind, __indexOf

雑多

prototypeの省略

'.prototype.'と記述するかわりに'::'と記述することができる

class A
A::f = ->
javascriptの埋め込み

'`'で囲うことで、javascriptをそのまま埋め込むことができる。

f = `function() {
  return "hoge"
}`
f() #=> "hoge"
soak existential operator

'?'を使用すると、対象がnull, もしくはundefinedではないことを確認することができる
変数の後ろに付けると存在を確認し、結果をboolとして返す

if var? then true

二項演算子としても使用でき、その場合左辺が存在していなければ右辺が返る

var ? "hoge" #=> 'hoge'
var = 1
var ? "hoge" #=> 1

代入との組合せで'?='も使用できる
以下は変数が定義さていなかったら空のオブジェクトを代入する例

var = var ? {}
var ?= {}
?. 変数の存在を確認
?:: 変数の存在を確認
?[] 変数の存在を確認
?() 関数の存在を確認
hoge?.fuga
hoge?::fuga
hoge?[1]
hoge?()
{var} = hoge

以下は同じ意味

{x} = obj
x = obj.x
代入文でのdestructuring

配列やオブジェクトの構造を書くと、対応する場所の変数に代入される。再帰的に書くこともできる
詳しくはECMAScript harmonyのdestructuringを参照

[swapA, swapB] = [swapB, swapA]
[{width:w, height:h}, hoge] = [{width:1, height:2}, 3]
w #=> 1
h #=> 2
hoge #=> 3
terminator

';'で一行に続けて書くことができる

1 + 2; 3 + 4

不完全な文法で無理矢理改行したい場合は'\'が使用できる
以下の例は問題無くparseできる

n = 1 \
  + 2
未調査

OptCommaの説明
{}や()の補完ルール
左辺に関数呼び出しが含まれる場合のルール
undefinedとvoid 0
代入文のchain
class methodでの=>
mixin (wiki FAQ)
制御構文が値を返す件の具体例
returnの補完ルール
replで変数を含むrangeが正常に解釈されない問題