JavaScriptのクラス定義を簡単にする

TypeScript (と TypeScript コンパイラ)を用いて JavaScript で利用可能なクラスの定義を作成(生成)することができます。
各サンプルはシンプルな HTML ファイル(index.html)と JavaScript ファイル(app.js)の２ファイルの構成です。ただし、app.js は app.ts (TypeScript 言語で書かれたソースファイル) をコンパイルすることによって自動生成されるものとなっています。

* ちなみに私はエディター(開発環境)として Visual Studio Professional 2012 Update 4 を用いています。([TypeScript for Visual Studio 2012](http://www.microsoft.com/en-za/download/details.aspx?id=34790)と[Web Essentials 2012](http://visualstudiogallery.msdn.microsoft.com/07d54d12-7133-4e15-becb-6f451ea3bea6)をインストールしています)

* index.html で jquery.min.js を script タグでインクルードしていますが、今回のサンプルの範疇では jQuery は用いていません。(次回以降で用います)

## HTML ファイル(共通)
```html:index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>TypeScript HTML App</title>
    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
    <script src="app.js"></script>
</head>
<body>
    <h1>TypeScript HTML App</h1>
    <div id="content"></div>
</body>
</html>
```

## サンプル[その１]：最も単純なクラス定義

* TypeScript では class キーワードを用いてクラス定義ができます。

```ts:app.ts(その１)
function writeln(msg) {
    console.log(msg);
}

//最も単純なクラス
class HelloWorld {
    private message;
    constructor(message) {
        this.message = message;
    }
    hello() {
        writeln("ハロー " + this.message);
    }
}

window.onload = function () {
    var v1 = new HelloWorld("TypeScript");
    v1.hello();
};
```

* `private message;`の行でクラスのメンバー変数(プロパティ)を定義しています。private も public も指定しないと public として扱われますので、private にしたいときは明示的に private を指定する必要があります。

```text:実行結果(その１：ブラウザのコンソール出力)
ハロー TypeScript
```

* 自動生成された app.js は完全な(スタンドアローンの) JavaScript ソースファイルですので、TypeScript のソースも TypeScript コンパイラもいらずに、Webサーバーに単独でアップできます。ただし、可読性とメンテナンスの容易さの観点から TypeScript のソースをメンテナンスしてコンパイラで JavaScript を生成するようにしたほうが得策であるケースが多いでしょう。

```js:app.js(その１：app.tsから自動生成されたもの)
function writeln(msg) {
    console.log(msg);
}

//最も単純なクラス
var HelloWorld = (function () {
    function HelloWorld(message) {
        this.message = message;
    }
    HelloWorld.prototype.hello = function () {
        writeln("ハロー " + this.message);
    };
    return HelloWorld;
})();

window.onload = function () {
    var v1 = new HelloWorld("TypeScript");
    v1.hello();
};
```

## 暗黙に定義されるメンバー変数=サンプル [その２]

* この例の app.ts では、`private message;`の行が削除されています。その代わりに、`constructor(private message) {}`の引数宣言の部分で変数名の前に private を付けています。このような記法を用いることで、メンバー変数を暗黙に定義することができます。`this.message = message;` の行も削除されていますがメンバー変数にコンストラクタの引数の値を代入する処理も暗黙に行われますので記述する必要がありません。このようにコンストラクタの引数を即座にメンバー変数に格納して初期化するケースは少なくないので(メンバー変数の暗黙の定義と初期化を)利用してみてください。

```ts:app.ts
function writeln(msg) {
    console.log(msg);
}

//暗黙に定義されるメンバー変数(プロパティ)
class HelloWorld {
    constructor(private message) {}
    hello() {
        writeln("ハロー " + this.message);
    }
}

window.onload = function () {
    var v1 = new HelloWorld("TypeScript");
    v1.hello();
};
```

## JavaScript ファイル(自動生成されたもの) サンプル[その２]

* 生成された app.js を見ると(コメント以外は)[その１]で生成されたものと同じ内容になっています。メンバー変数の暗黙の宣言と初期化が機能しているからです。

```js:app.js
function writeln(msg) {
    console.log(msg);
}

//暗黙に定義されるメンバー変数(プロパティ)
var HelloWorld = (function () {
    function HelloWorld(message) {
        this.message = message;
    }
    HelloWorld.prototype.hello = function () {
        writeln("ハロー " + this.message);
    };
    return HelloWorld;
})();

window.onload = function () {
    var v1 = new HelloWorld("TypeScript");
    v1.hello();
};
```

## クラスメソッドとインスタンスメソッド＝サンプル[その３]

```ts:app.ts
//クラスメソッド(staticメソッド)の例
class MyMath {
    static add(x, y) {
        return x + y;
    }
    static subtract(x, y) {
        return x - y;
    }
}

//インスタンスメソッドの例
class MySquare {
    //一辺の長さで初期化
    constructor(private length) { }
    //面積を求める
    area() {
        return this.length * this.length;
    }
}

window.onload = function () {
    console.log("add()=" + MyMath.add(11, 22));
    console.log("subtract()=" + MyMath.subtract(11, 22));
    var square = new MySquare(3);
    console.log("area()=" + square.area());
};
```

```text:実行結果(その３：ブラウザのコンソール出力)
add()=33
subtract()=-11
area()=9
```

* クラスメソッドとインスタンスメソッドがそれぞれどのような JavaScript コードに変換されたかをみてみましょう。

```js:app.js
//クラスメソッド(staticメソッド)の例
var MyMath = (function () {
    function MyMath() {
    }
    MyMath.add = function (x, y) {
        return x + y;
    };
    MyMath.subtract = function (x, y) {
        return x - y;
    };
    return MyMath;
})();

//インスタンスメソッドの例
var MySquare = (function () {
    //一辺の長さで初期化
    function MySquare(length) {
        this.length = length;
    }
    //面積を求める
    MySquare.prototype.area = function () {
        return this.length * this.length;
    };
    return MySquare;
})();

window.onload = function () {
    console.log("add()=" + MyMath.add(11, 22));
    console.log("subtract()=" + MyMath.subtract(11, 22));
    var square = new MySquare(3);
    console.log("area()=" + square.area());
};
```

* コンパイラによって変換(自動生成)されたコードを参考にして、JavaScript で直接クラス定義を書くことも可能ですが、開発環境が整えば TypeScript (class キーワード構文) を用いてクラス定義を行うことでメンテナンス性が上がったり、開発効率が上がることもあるでしょう。

## 最後に

* 最後にものすごく長い名前空間を用いたクラス定義を挙げておきます。

```ts:app.ts
module Long.Long.Annoying.Namespace {
    export class Vector2 {
        constructor(public x: number, public y: number) { }
    }
}
```

```js:app.js
var Long;
(function (_Long) {
    (function (Long) {
        (function (Annoying) {
            (function (Namespace) {
                var Vector2 = (function () {
                    function Vector2(x, y) {
                        this.x = x;
                        this.y = y;
                    }
                    return Vector2;
                })();
                Namespace.Vector2 = Vector2;
            })(Annoying.Namespace || (Annoying.Namespace = {}));
            var Namespace = Annoying.Namespace;
        })(Long.Annoying || (Long.Annoying = {}));
        var Annoying = Long.Annoying;
    })(_Long.Long || (_Long.Long = {}));
    var Long = _Long.Long;
})(Long || (Long = {}));
```

* これでもあなたは JavaScript で直接クラス定義をし続けますか？

第一回終了～！

## 参考

* [JavaScript の構文拡張: TypeScript (基本型)](http://qiita.com/akmiyoshi/items/66b8fa67bcd0aed192cc)

* [CoffeeScript と TypeScript の開発環境](http://qiita.com/akmiyoshi/items/6b8cd9680d611cde6251)