CoffeeScript 入門

CoffeeScript とは、JavaScript の文法をより簡単に書くための記法です。インデントに従って記述します。ネイティブとの主な違いをあげると、

・変数宣言時に const, let はいりません。
・function( ) は、( )-> で書くことができます(アロー関数)。
・関数の引数に( ) はいりません。代わりに半角スペースを1つ空けます。(ex. console.log ‘test’)
・関数の返り値に return はいりません。
・this は @ の1文字で書くことができます。
・インデントによってブロックを表現します。なので、クラス、関数、if 文などで { } はいりません。

全体的にタイプ数が減るので開発効率が上がるのと、インデントによってコードの可読性が増します。また、 == の演算子を自動的に === に変換して安全性を強化したり、ネイティブにはない便利な文法も豊富に用意されているのでかなりおすすめです。

変数

a = 'hoge'

文字列内で式を展開

“”(ダブルクォーテーション) 内で #{ } を使用します。

a = 'hoge'
"#{a}" # hoge

Block Regular Expressions

/// を利用することで、正規表現リテラルでも変数を使用することができます。

a = 'hoge'
result = ///#{a}///.test 'hoge'
console.log result # true

コメント

# から行末までがコメントになります。

# コメント行

評価値が false になるパターン

以下の場合は false になります。

・ null
・ undefined
・ 0
・ ''
・ false

便利な演算子

** でべき乗の計算ができます。

2 ** 3 # 8

// で割り算の商を整数で返します。

10 %% 6 # 1

%% で割り算のあまりを整数で返します。

10 %% 6 # 4

いくつかの演算子でエイリアス(別名)が使用できます。

演算子 エイリアス
=== is
!== isnt
! not
&& and
|| or
a = 10
if a is 10 
  console.log 'aは10です'

CoffeeScript での ?

CoffeeScript における ? には、「存在確認演算子」「論理演算子」「拡張論理演算子」の3つの機能があります。

存在確認演算子

変数? という形で使用すると、 変数が null,undefined の場合は false、それ以外なら true を返します。if 文の条件式でよく利用します。

a = 'hoge'
console.log a? # true

b = null
console.log b? # false

また、オブジェクトの変数参照時やメソッドチェーンの途中でオブジェクトが null, undefined の場合でも、エラーにせずに undefined を返します。

console.log obj?.a # undefined

論理演算子

a ? b という形で使用すると、a が null, undefined の場合は b を返します。

console.log null ? 1 # 1
console.log undefined ? 1 # 1

拡張論理演算子

a ?= b という形で使用すると、a が null, undefined の場合は a に b を代入します。

a = null
b = 2
a ?= b

console.log a # 2

a = 1
b = 2
a ?= b

console.log a # 1のまま

配列

arr = [1, 2]

# 範囲指定(1から10まで)
arr = [1..10] # [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 追加
[1, 2].push 3 # [1, 2, 3]

# 結合
[1, 2].concat [3, 4, 5] # [1, 2, 3, 4, 5]

# 特定の値のインデックス取得
[1, 2, 3].indexOf 2 # 1

# 特定のインデックス要素を削除
arr.splice index, 1

# 最後の要素を削除
arr = [1, 2, 3]
arr.pop()

# 連結して文字列を作る
[1, 2, 3].join '-' # "1-2-3"

map
各要素に対して何らかの処理をしてから新しい配列を作ります。
array は元の配列です。(index と array は省略可)

# 各要素に2をかける
[1, 2].map (element, index, array)-> element * 2 # [2, 4]

filter
条件に合う要素のみ抽出します。array は元の配列です。(index と array は省略可)

# 2の倍数を抽出
[1, 2, 3, 4, 5].filter (element, index, array)-> element % 2 == 0 # [2, 4]

reduce
要素の最初から取り出していき、最終的に1つの値を算出します。
currentValue は現在取り出している要素、previousValue はそれまでに処理した結果の合計値です。

# 各要素を足していく
[1, 2, 3].reduce (previousValue, currentValue, index, array)-> previousValue + currentValue
# 6

reduceRight にすると、要素の最後から取り出します。

every/some
every は全ての要素がある条件を満たすかどうか、some は条件を満たす要素があるかを true/false で返します。array は元の配列です。(index と array は省略可)

[1, 2, 3].every (element, index, array)-> element > 5 # false
[1, 2, 3].some (element, index, array)-> element < 2 # true

連想配列

# ハッシュは自動で { } がつきます
obj = a: 1, b: 2
# またはインデントを利用して
obj = 
  a: 1
  b: 2

# キーのみ配列で取り出す
Object.keys obj # ["a", "b"]

# 追加
obj['c'] = 3
obj.c = 3

# 結合
Object.assign(obj, c: 3, d: 4) # {a: 1, b: 2, c: 3, d: 4}

# 特定のキーを削除
delete obj['c']
delete obj.c

Map

Key-Value でデータを扱うクラスです。キーに Object を指定することもできます。

map = new Map()

# 値をセットする
map.set 'a', 1
map.set 'b', 2

# 初期化と同時にセットする場合
map = new Map [['a', 1], ['b', 2]]

# 値を取り出す
map.get 'a'

# キーの存在確認
map.has 'a'

# キーの数
map.size

# 特定キーの削除
map.delete 'a'

# キーの全削除
map.clear()

Set

値が重複しない配列を扱うクラスです。同じ値を追加しようとしても無視されます。

set = new Set()

# 値をセットする
set.add 1
set.add 2

# 初期化と同時にセットする場合
set = new Set [1, 2]

# 値の存在確認
set.has 1

# 値の数
set.size

# 特定の値を削除
set.delete 1

# 値の全削除
set.clear()

三項演算子

if 条件式 then 式1 else 式2の形で使用し、条件式 が true の場合は 式1、false の場合は 式2 を返します。

num = 30
console.log(if num >= 20 then '20以上です' else '20未満です') # 20以上です

条件分岐

if の場合

if (条件)
  ...


# 1行で書く場合
... if 条件式

if 条件式
  ...
else
  ...


if 条件式1
  ...
else if 条件式2
  ...
else
  ...

switch の場合

a = 10
switch a
  when 1 
    console.log '1'
  # 複数指定
  when 2, 3 
    console.log '2 or 3'
  # それ以外
  else 
    console.log 'else'

then を利用して1行で書くことも可能です。

a = 10
switch a
  when 1 then console.log '1'
  when 2, 3 then console.log '2 or 3'
  else console.log 'else'

ループ

配列 の場合

# for
for i in [1, 2, 3]
  console.log i # 1, 2, 3

# for 範囲指定
for i in [1..3]
  console.log i # 1, 2, 3

# for-in
for num, index in [1,2,3]
  console.log num # 1, 2, 3

# for-in 1行で
console.log(num) for num in [1, 2, 3] # 1, 2, 3
console.log(num) for num, index in [1, 2, 3] # 1, 2, 3. 範囲指定だとエラーになるので注意

# for-in 条件付き
console.log(num) for num in [1..10] when num < 5 # 1, 2, 3, 4

# forEach
[1, 2, 3].forEach (val)->
  console.log val # 1, 2, 3

# forEach indexつき
[1, 2, 3].forEach (val, i)->
  console.log val # 1, 2, 3
  console.log i # 0, 1, 2

オブジェクト の場合

# key のみ
for key of {a: 1, b: 2}
  console.log key # "a", "b"

# key, value
for key, value of {a: 1, b: 2}
  console.log value # 1, 2

Map の場合

map = new Map [['a', 1], ['b', 2]]
map.forEach (value, key)->
  console.log key # "a", "b"
  console.log value # 1, 2

Set の場合

set = new Set [1, 2]
set.forEach (val)->
  console.log val # 1, 2

while 文

# 条件式が false になるまでループする
while 条件式
  ...


a = 0
while a < 3
  console.log a # 0, 1, 2
  a++

break / continue
ループ処理を途中でやめる時に使用します。 breakはループ処理自体を完全にやめる場合、continue は次のループに移る場合に使用します。

a = 0
while a < 10   
  if a == 3
    break
  console.log a # 0, 1, 2 
  a++

分割代入

配列やオブジェクトの値を個別に取り出して、変数に入れることができます。

# 配列
arr = [1, 2, 3]
[a, b] = arr
console.log a # 1
console.log b # 2

# 初期値を設定する場合
arr = [1,2,3]
[a, b, c, d = 4] = arr
console.log d # 4

# オブジェクト
obj = {a: 1, b: 2, c: 3}
{a, b} = obj
console.log a # 1
console.log b # 2

# 任意のキー名に設定する場合
{a: x, b: y} = obj

console.log x # 1
console.log y # 2

# 初期値を設定する場合
{a, b, e = 5} = obj
console.log e # 5

クラス

class User
  @age: 10 # クラス変数
  email: 'tanaka@example.com' # インスタンス変数
  _role = 'manager' # プライベート変数. _role: 'manager' と宣言しないこと

  constructor: (name)->
    @name = name
   
  # インスタンスメソッド
  hello: -> 
    console.log "Hello, #{@name}"
    console.log @email
  
  # クラスメソッド
  @hello: ->
    console.log 'Hello'
    console.log @age
    
  # プライベートメソッド
  _privateHello = ->
    console.log 'Private Hello'
         
user = new User('Tanaka')
user.hello() 
# "Hello, Tanaka"
# "tanaka@example.com"

User.hello()
# "Hello"
# 10

クラスを継承することもできます。

# 親クラス
class Animal
  constructor: (name)->
    @name = name
 
  hello: ->
    "Hello, #{@name}"

# 子クラス
class Cat extends Animal
  hello: ->
    # 親クラスの同名メソッドは super で呼び出す
    console.log super

cat = new Cat('Tama')
cat.hello() # "Hello, Tama"

関数

# 引数なし
# 処理が1行のみならワンライナーで書くことができます
hello = -> 'Hello'
   
hello() # "Hello"

# 引数あり
add = (a, b)-> a + b

add 1, 2 # 3

# 引数のデフォルト値あり
add = (a, b = 5)-> a + b

add 1 # 6

# 無名関数(即時関数)
do ->
  console.log 'すぐに実行されます'

# 可変長引数
add = (a, b...)->
  console.log a # 1
  console.log b # [2, 3, 4, 5]

add 1, 2, 3, 4, 5

javascript には、関数の中で関数を定義すると、内側の this がグローバルオブジェクトを指すという仕様があります。

obj =
  test: ->
    console.log @ # [object Object]
    do ->
      console.log @ # [object Window]

しかし、-> の代わりに =>(ファットアロー) を使用すると、thisが自身のオブジェクトを指し続けるようになります。

obj =
  test: ->
    console.log @ # [object Object]
    do =>
      console.log @ # [object Object]

import/export

変数や関数、クラスをモジュールとして定義することができます。これにより、あるファイルから別のファイルで定義された変数・関数・クラスを呼び出すことができます。

モジュールの定義

exportをつけて定義します。例として、module.js というファイルに以下の3つのモジュールを定義してみます。

# 変数
expor num = 1

# 関数
export add = (a, b)-> a + b

# クラス
export class Greeting
  hello: -> 'Hello'

# デフォルトモジュール
# importする際に特に指定がなければ default がついたモジュールが呼ばれます
export default (a, b)-> a - b

※ デフォルトモジュールは、関数またはクラスでのみ利用でき、変数はエラーになります。

export default function ...
export default class ...
export default 変数名 ...

モジュールの呼び出し

importで呼び出します。呼び出し方にはいろいろなパターンがあります。

import * as mod from './module.js' # 全モジュールを1つの変数で読み込む場合
import substract from './module.js' # デフォルトモジュールのみ読み込む場合
import { num, add } from './module.js' # モジュールを個別に読み込む場合
import './module.js' # 読み込むだけの場合. cssなど

mod.num # 1
mod.add 1, 2 # 3

greeting = new mod.Greeting()
greeting.hello() # "Hello"

substract 10, 2 # 8

add 10, 20 # 30