JS リーディング@20120217

今日は社内で毎週開かれている JS リーディングに参加しました。今日の参加者は僕も含めて4人。
今日は The Little Book on CoffeeScript をみんなで読んでみようということになりました。
まぁ本当はこれくらいは一人で週末とかでさくっと読めよ、という話なんですが、わいわい言いながら読むのはまた違う趣があります。


今日は 2.文法 のフローコントロールの最後まで読みました。
該当の箇所を読めば全部分かるので、書くほどでもないですが、気になったところをいくつか。

  • コメント
    • 一行のコメントは生成された JS には残らない。複数行のコメントは残る
  • Coffee は空白 (インデント) に意味を持つ (Python のように)
  • 変数とスコープ
    • JavaScript GoodParts で推奨されていたような、全て var 宣言された変数が生成された一番上に列挙される
    • グローバル変数は基本的には作られない
      • グローバル変数を使いたい場合は Window オブジェクトのプロパティとして代入するか、以下のようなイディオムで使う
exports = this
exports.MyVariable = "foo-bar"
  • 関数
    • -> が function
func = -> "bar" // は以下のようになる

var func;
func = function() {
      return "bar";
};
-> "bar" // で無名関数

(function() {
      return "bar";
});
  • 関数引数
times = (a, b) -> a * b 
times = (a = 1, b = 2) -> a * b // デフォルト引数が持てる。通常の JS ではできない。
# でも生成される JS は泥臭いですね^^
var times;
times = function(a, b) {
  if (a == null) {
    a = 1;
  }
  if (b == null) {
    b = 2;
  }
  return a * b;
};
    • 可変長引数は ... (スプラットと読むらしい、知らなかった) で表現できる
sum = (nums...) ->
  result = 0
  nums.forEach (n) -> result += n
  result
    • これは以下のように生成される
var sum;
var __slice = Array.prototype.slice;
sum = function() {
  var nums, result;
  nums = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
  result = 0;
  nums.forEach(function(n) {
    return result += n;
  });
  return result;
};
  • __slice (__*) は coffee の予約語になり宣言できない
    • Array.prototype.slice を なぜ __slice に一度いれなければいけないかは分からなかった
      • __ でメソッドを定義して、生成する感じ、utility 関数的に宣言を行って可読性をあげるため?
    • nums = 1 <= arguments.length ? __slice.call(arguments, 0) : []; で arguments オブジェクトではなく、本物の配列として扱う
  • 関数の実行
    • 以下3つはすべて同じ。最低でも一番下の例のような inspect の実行には括弧を付けることを推奨
alert inspect a
alert(inspect(a))
alert inspect(a)
  • 関数コンテキスト
    • => (ファットアロー) を使うことで、関数のコンテキスト (this) をローカルに紐付けることが可能になる
this.clickHandler = -> alert "clicked"
element.addEventListener "click", (e) => this.clickHandler(e)

// 上の例は以下のように出力されるとあるが
var __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
this.clickHandler = function() {
  return alert("clicked");
};
element.addEventListener("click", __bind(function(e) {
  return this.clickHandler(e);
}, this));

// http://coffeescript.org/ で試すと、出力される結果が異なる __bind を出さなくなった?
var _this = this;

this.clickHandler = function() {
  return alert("clicked");
};

element.addEventListener("click", function(e) {
  return _this.clickHandler(e);
});
  • CoffeeScriptを使うべきか、使わざるべきか? - にのせき日記 でもファットアロウについて言及があるが、もし jQuery を使っているなら $.proxy を普通に使うことも可能
  • オブジェクトの構文と配列の定義
    • 中括弧なしで書ける。User.create(name: "John Smith") これはいい。
    • 配列はインデントをカンマの区切りとして使える。Ruby の %w のような。
  • フローコントロール
    • 三項演算子はなくて、if 1 > 0 then "Ok" else "Y2K!" if/else 文を使う必要がある
      • Python2.5 からの記法とちょっと似てる A = Y if X else Z
a = if foo then "bar" else "baz" # が以下のようになる

var a;
a = foo ? "bar" : "baz";

a = foo ? "bar" : "baz" // 三項演算子を上のように coffee で書くとは以下のように出力される
a = typeof foo !== "undefined" && foo !== null ? foo : {
  "bar": "baz"
};
  • 後置演算子や unless もある
    • == と Coffee で書くと === に変換される
      • != も !== になる
        • JS で書かれたスクリプトを Coffee に変換する時は、== で書いてても、=== になる? バグがでそう