homeASCIIcasts

267: CoffeeScriptの基礎 

(view original Railscast)

Other translations: En It Es

Other formats:

Written by Naomi Fujimoto

CoffeeScriptは、コンパイルしてJavaScriptを生成する言語です。Rails 3.1に含まれたので、もうすぐ多くのRails開発者が初めて目にすることになるでしょう。今回のエピソードでは、既存のJavaScriptコードをCoffeeScriptに変換していきます。これがこの言語を知る有効な方法です。変換するJavaScriptは、エピソード261 [動画を見る, 読む]で書いたクレジットカード番号を検証するコードです。

今までにCoffeeScriptを使ったことがない場合は、CoffeeScriptのウェブサイトから始めるのがいいでしょう。そこで、いくつのCoffeeScriptコードの例とそれぞれをコンパイルしたJavaScriptを見ることができます。それに加え、サイトにはCoffeeScriptコードを入力してその場でコンパイルされたJavaScriptを確認できるページもあります。コンパイルされたJavaScriptはブラウザで実行することができ、これらはサーバへのAJAX呼び出しを行うことなくすべてクライアント側で実行されます。

これから変換するJavaScriptコードは、下のページで使われているものでユーザがクレジットカード番号フィールドからタブキーで抜けるときに起動されます。これは、入力された番号に対してmod 10を用いて基本的な検証を行い、番号が不正だった場合にテキストフィールドの横にメラーメッセージを表示します。

クレジットカード検証ページ

これを行うJavaScriptは以下のとおりです。

var CreditCard = {
  cleanNumber: function(number) {
    return number.replace(/[- ]/g, "");
  },
  
  validNumber: function(number) {
    var total = 0;
    number = this.cleanNumber(number);
    for (var i=number.length-1; i >= 0; i--) {
      var n = +number[i];
      if ((i+number.length) % 2 == 0) {
        n = n*2 > 9 ?n*2 - 9 : n*2;
      }
      total += n;
    };
    return total % 10 == 0;
  }
};

$(function() {
  $("#order_credit_card_number").blur(function() {
    if (CreditCard.validNumber(this.value)) {
      $("#credit_card_number_error").text("");
    } else {
      $("#credit_card_number_error").text("Invalid credit card number.");
    }
  });
});

なお、このアプリケーションで使用しているRailsのバージョンは、この記事の執筆時の最新版である3.1 RC1です。gem install rails --preを実行することでアップグレードが可能です。

最初の変更

JavaScriptファイルをCoffeeScriptファイルに変えるには、ファイル名に.coffeeを追加します。Rails 3.1のアプリケーションでも、拡張子を.jsのままにしておけば引き続きJavaScriptファイルを使用することができます。CoffeeScriptの使用は完全に任意です。

まずJavaScriptファイルのコードをコメントアウトして、一つずつそれに対応するCoffeeScriptに置き換えていきます。最初に変換する部分は、スペースやハイフンを除いて入力された番号を整形するcleanNumber関数にします。

/app/assets/javascripts/orders.js.coffee

var CreditCard = {
  cleanNumber: function(number) {
    return number.replace(/[- ]/g, "");
  }
}

対応するCoffeeScriptのコードは以下のようになります。

/app/assets/javascripts/orders.js.coffee

CreditCard =
  cleanNumber: (number) ->
    number.replace /[- ]/g, ""

CoffeeScriptでは、多くのJavaScriptの構文を取り除くことができます。CoffeeScriptはタブでブロックレベルを定義するので、すべての波カッコを削除できます。これはつまりスペースの使い方に一貫性が必要になるということです。

さらにvarキーワードが不要になるためすべて削除します。関数の最後の部分のreturnキーワードも不要です。これはちょうどRubyと同じように、関数の最後の値が自動的に返されるからです。セミコロンもCoffeeScriptでは不要なため削除できます。

引数を渡す関数呼び出しでは、引数をカッコで囲む必要がなくなるのでこれも削除します。例外は引数を持たない関数を呼び出すときです。この場合は関数が呼び出されたことをCoffeeScriptに知らせるためにカッコが必要です。

最後に関数の呼び出し方を変更します。functionキーワードを削除して、代わりに関数の引数の後に->を付けます。慣れるまでに少し時間がかかるかも知れませんが、これはCoffeeScriptで関数を定義する簡便な方法です。

この部分を変更したら、コンパイルしてどのようなJavaScriptを生成するかを見てみます。出力されたものは、元のコードに非常に似ています。

var CreditCard;
CreditCard = {
  cleanNumber: function(number) {
    return number.replace(/[- ]/g, "");
  }
};

次にこのJavaScriptコードの中では、大きな方であるvalidNumber関数を見てみましょう。

/app/assets/javascripts/orders.js.coffee

validNumber: function(number) {
  var total = 0;
  number = this.cleanNumber(number);
  for (var i=number.length-1; i >= 0; i--) {
    var n = +number[i];
    if ((i+number.length) % 2 == 0) {
      n = n*2 > 9 ?n*2 - 9 : n*2;
    }
    total += n;
  };
  return total % 10 == 0;
}

同じような手順でこのコードを書き換えていくと、次のCoffeeScriptができ上がります。

/app/assets/javascripts/orders.js.coffee

validNumber: (number) ->
  total = 0
  number = @cleanNumber(number)
  for i in [(number.length-1)..0]
    n = +number[i]
    if (i+number.length) % 2 == 0
      n = if n*2 > 9 then n*2 - 9 else n*2
    total += n
  total % 10 == 0

前と同じように波カッコとセミコロンを取り除きました。varreturnキーワードをとり、function->に置き換えました。コードをきれいにするためにさらにその他の変更を加えました。

thisへの参照があったら@記号に置き換えることができるので、this.cleanNumber@cleanNumberになります。if文の外側のカッコも不要なので取り除きます。三項演算子も変わったので、C言語タイプの疑問符とコロンをif then else構文に置き換えて、ifを最初に置いて、疑問符をthenに変更し、コロンをelseに変更します。

コードの他の部分は良さそうです。後はforループを変えるだけです。CoffeeScriptによる繰り返し処理はJavaScriptと異なるため、コードを変更する前に確認しておきましょう。配列に対する繰り返しはこのようなコードになります。

for number in [1,2,3]
 alert number

これが以下のJavaScriptを生成します。

var number, _i, _len, _ref;
_ref = [1, 2, 3];
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
  number = _ref[_i];
  alert(number);
}

またはもう一つの方法として、以下のように書いても同じJavaScriptを生成します。

alert number for number in [1,2,3]

連続した数字の場合は、配列の代わりに範囲を使うことができます。

for number in [1..3]
 alert number

これによって、生成されるJavaScriptをより単純化できます。

var number;
for (number = 1; number <= 3; number++) {
  alert(number);
}

ループのカウントを増やすのではなく減らしたい場合は、範囲で指定する数字を逆にします。

for number in [3..1]
 alert number

これはforループの振る舞いと似ているので、同じようなカウントダウンと置き換えられます。

JavaScriptコードの最後の残りの部分に進みます。

/app/assets/javascripts/orders.js.coffee

$(function() {
  $("#order_credit_card_number").blur(function() {
    if (CreditCard.validNumber(this.value)) {
      $("#credit_card_number_error").text("");
    } else {
      $("#credit_card_number_error").text("Invalid credit ↵
        card number.");
    }
  });
});

このjQueryのコードは、クレジットカード番号フィールドのblurイベントに検証用コードを付加します。CoffeeScriptでjQueryコードを扱う場合にも特別な処理は不要です。対応するCoffeeScriptのコードは以下のようになります。

/app/assets/javascripts/orders.js.coffee

jQuery ->
  $("#order_credit_card_number").blur ->
    if CreditCard.validNumber(@value)
      $("#credit_card_number_error").text("")
    else
      $("#credit_card_number_error").text("Invalid credit ↵
        card number.")

前と同じように波カッコとセミコロンを取り除き、function->に、thisへの参照を @にそれぞれ置き換えます。もう一つ行った変更は、最初の$の呼び出しをjQueryに変えたことです。これは機能には影響はないのですが、これによりjQueryを使用していることが明確になります。

JavaScriptコードをすべてCoffeeScriptに変更できたので、ブラウザでページを再読み込みし正しく表示されるか見てみましょう。

CoffeeScriptがJavaScriptと同じように表示された

うまくいきました。無効なクレジットカード番号を入力するとエラーメッセージが表示され、有効な番号を入力するとメッセージが消えます。生成されたJavaScriptファイルの最後の部分には、CoffeeScriptファイルからコンパイルされたJavaScriptを見ることができます。

http://localhost:3000/assets/application.js

(function() {
  var CreditCard;
  CreditCard = {
    cleanNumber: function(number) {
      return number.replace(/[- ]/g, "");
    },
    validNumber: function(number) {
      var i, n, total, _ref;
      total = 0;
      number = this.cleanNumber(number);
      for (i = _ref = number.length - 1; _ref <= 0 ?i <= 0 : ↵
        i >= 0; _ref <= 0 ?i++ : i--) {
        n = +number[i];
        if ((i + number.length) % 2 === 0) {
          n = n * 2 > 9 ?n * 2 - 9 : n * 2;
        }
        total += n;
      }
      return total % 10 === 0;
    }
  };
  jQuery(function() {
    return $("#order_credit_card_number").blur(function() {
      if (CreditCard.validNumber(this.value)) {
        return $("#credit_card_number_error").text("");
      } else {
        return $("#credit_card_number_error").text("Invalid ↵ 
          credit card number.");
      }
    });
  });
}).call(this);

デバッグ

CoffeeScriptのコードに文法エラーがあった場合はどうなるでしょうか? CoffeeScriptファイルをわざと間違えて記述して、そのページをブラウザで見ても、JavaScriptリクエストは別なのでエラーは表示されません。しかし開発者用コンソールを見てみると、エラーがリスト表示されています。

CoffeeScriptのエラーがブラウザの開発者用コンソールに表示される

エラーメッセージを見れば、何行目のコードの何が問題かについて十分な情報があるので、コードのどこをデバッグすればいいかがわかります。

今回のエピソードは以上です。CoffeeScriptについてここで触れられなかった点もたくさんあるので、CoffeeScriptのサイトでこの楽しい小さな言語についてさらに学んでみてはいかがですか?