LoginSignup
632
621

More than 5 years have passed since last update.

CoffeeScriptを使う理由

Last updated at Posted at 2014-05-03

随時更新します。

  1. CoffeeScriptを使う理由
  2. TypeScriptを使う理由
  3. AltJSを使わない理由
  4. Dartを使う理由

仕事でCoffeeScriptを使う場合の説得材料まとめです。

なぜJavaScriptではいけないのか

クラス定義がないからです

みんな大好きオブジェクト指向をするために必須なのにJavaScriptではクラスは書けません。
いや、正確には書けます。

クラス定義
function Person() {};

なんだクラスは簡単に書けるじゃないかと思いましたか?


クラス定義その2
function Person() {
  this.name = "takashi";
  this.age = 20;
};
Person.prototype.echo = function () {
  return "Hello " + this.name + " !!";
};

コンストラクタ、メソッドを書いただけで、コード量が増えてきましたね。
毎回クラス名とprototypeと書く必要があります。
めげてきましたか?
私はめげてます。


次は継承してみましょう。

継承
function inherits(ctor, superCtor) {
  ctor.super = superCtor;
  ctor.prototype = Object.create(superCtor.prototype, {
    constructor: {
      value: ctor,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
};

inherits(Employee, Person);
function Employee() {
  Person.apply(this, arguments);
};

Employee.prototype.echo = function () {
  return "Hello " + Employee.super.prototype.echo.call(this);
};

どうでしょうか?
継承元を呼び出すのにEmployee.super.prototype.echo.call(this)のように書かないといけません。

継承の方法は今まで迷走していたので各々実装方法も違います。
詳しく知りたい場合はこちらを参照するといいです。
そんな継承はイヤだ - クラス定義 - オブジェクト作成

勘違いしてしまうfor文

以下のコードは間違っています。

間違ったfor...in
var ary = [100, 200, 300];
for (var i in ary) {
  console.log(i);
}

JavaScriptでinは配列のeachではありません。
オブジェクトのプロパティ名をeachする命令です。

正しいfor...in
var obj = {key1: 100, key2: 200, key3: 300}
for (var key in obj) {
  console.log(obj[key]);
}
正しい配列のfor
var ary = [100, 200, 300];
for (var i = 0 ; i < ary.length ; i++) {
  console.log(ary[i]);
}

functionという記述が長過ぎる

JavaScriptはシングルスレッドなので、callbackを多用します。
多用するのに毎回functionと書かなければいけません。
後からコードを見直すとfunctionだらけで頭が痛くなることでしょう。

functionが長過ぎる
var self = this;
$.get("/users", function (users) {
  var result = [];
  $.each(users, function (user) {
    result.push(new User(user));
  });
  return result;
}).done(function (users) {
  self.users = users;
}).fail(function (xhr, status, msg) {
  console.error(msg);
});

グローバル汚染を防ぐためにすることがある

functionで命令を閉じ込めてあげないとグローバルに定義されるので
他のライブラリとぶつかったりします。

varを書き忘れるとグローバルになるので他のライブラリと・・・

グローバル汚染をする?しない?
// グローバル汚染します
function Person() { // グローバル汚染
  name = "takashi"; // グローバル汚染
  this.word = "Hello " + name + " !!"; // でも動くよ
}
new Person();
name; // -> takashi

// グローバル汚染しません
(function () {
  function Person() {
    var name = "takashi";
    this.word = "Hello " + name + " !!";
  }
  new Person();
})();
name; // undefined

比較が曖昧

比較時に勝手に変換すると予期せぬ挙動となります。
なので毎回===と書かなくてはいけない。

比較例
""           ==   "0"           // false
0            ==   ""            // true
0            ==   "0"           // true
false        ==   "false"       // false
false        ==   "0"           // true
false        ==   undefined     // false
false        ==   null          // false
null         ==   undefined     // true
" \t\r\n"    ==   0             // true

CoffeeScriptを使おう

これらのよくないコードを未然に防いでくれるのがCoffeeScriptです。
それぞれ例をみていきましょう。

クラス定義
class Person
  constructor: () ->
    @name = "takashi"
    @age = 20

  echo: () ->
    "Hello #{@name} !!"

class Employee extends Person
  constructor: () ->
    super

  echo: () ->
    "Hello #{super()}"
for文
# 配列のループ
ary = [100, 200, 300]
for value in ary
  console.log value

# プロパティのループ
obj = {key1: 100, key2: 200, key3: 300}
for key, value of obj
  console.log(value)
function文
$.get "/users", (users) =>
  result = [];
  $.each users, (user) =>
    result.push(new User(user))
  return result
.done (users) =>
  @users = users
.fail xhr, status, msg =>
  console.error(msg)
グローバル汚染しない
# CoffeeScriptコンパイル時にbare=trueで関数で囲んでくれる
class Person
  constructor: () ->
    @name = "takashi" # インスタンス変数
    @word = "Hello #{name} !!" # ローカル変数
}
new Person()
name # -> undefined
比較は厳密
""           ==   "0"           # false
0            ==   ""            # false
0            ==   "0"           # false
false        ==   "false"       # false
false        ==   "0"           # false
false        ==   undefined     # false
false        ==   null          # false
null         ==   undefined     # false
" \t\r\n"    ==   0             # false

どうですか?
ダメなコードも未然に防げて、かつ簡素なコードでしょう?

CoffeeScriptはコンパイルしてJavaScriptを出力するけど、デバッグはどうするの?

CoffeeScriptにはSourceMapを出力するオプションが存在します。
これは出力されたJavaScriptがCoffeeScriptのどのコードの位置とマッチするかの情報を含んだファイルです。
これをChromeに食わせることにより、CoffeeScriptでデバッグが行えます。

詳しいやり方はグーグルでCoffeeScript source map chromeとかで検索するといいです。

ですが私が今までCoffeeScriptで開発してきた経験からいうと、出力されたJavaScriptをそのままデバッグした方が楽にデバッグできます。

CoffeeScriptが何行かのJavaScriptを出力しているために、同じ行でデバッガーのNext Stepを何回する必要があるため、変なところで進んだり止まったりしてデバッグしにくいです。

consoleにそのままCoffeeScriptを貼付けて動作させることもできないので、わざわざ変換するのが面倒だったりします。
CoffeeConsole?あれは忘れた方がいい。

CoffeeScriptは別言語なのではなく、JavaScriptのシンタックスシュガーです。
CoffeeScriptとJavaScriptの命令は1対1で簡単に結びつきます。
なので出力されたJavaScriptで苦もなくデバッグすることができます。

ちょっと注意、魔法の杖ではありませんよ。

HibernateしかりActiveRecordしかり、ORMはDBの設計、SQLをある程度知っていて始めて使用することができます。
ORM使うからSQL知らなくても問題ないぜー!!バリバリー!!
みたいなことはしてませんよね?

CoffeeScriptもご多分に漏れずJavaScriptをある程度知っている必要があります。
いきなりCoffeeScriptから始めます!
というようなことは避けた方が懸命です。
JavaScriptを軽く学習させてからCoffeeScriptを学習させてください。
そうしないとデバッグで死にます。

わかりました。CoffeeScriptを使います。

ようこそCoffeeScriptの世界へ。

あたなはCoffeeScriptを採用したことで以下の恩恵を受けることができます。

  1. ソースコードが簡素になったことで保守性があがります。
  2. JavaScriptの罠に嵌らなくなります。

あれ?少ない?
いえいえ、すごく大事な2点です。
塵積です。

でも待って・・・実は問題山積みなんです。

CoffeeScriptはコンパイルが必要です。
このコンパイルを甘く見てはいけません。

やってみればわかります。めんどいです。

私もCoffeeScriptコンパイルジプシーです。

・・・この投稿はとりあえずここまで!!
次回私が行ったCoffeeScriptのコンパイル方法の変遷を紹介します。

書きました。
私のCoffeeScriptをコンパイルする方法の変遷

632
621
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
632
621