JavaScript モジュール・クラス総括（2015年7月）

ES6 a.k.a. ECMAScript 2015が承認されたので
JavaScriptのモジュールとクラスについて
ブラウザJavaScript(以下、ブラウザJS)からの目線で総括してみます。

#モジュール
##ブラウザJS（モジュールなし）
ES5以前にモジュールという仕様はないので
グローバルオブジェクト作成し
名前空間パターン
`var obj = obj || {}`
で
JSファイルの分割結合をできるように。

```js
// foo.js
var myProject = myProject || {};
myProject.Foo = function(){};
```
```js
// bar.js
var myProject = myProject || {};
myProject.Bar = function(){};
```
```js
// module.js
var myProject = myProject || {};
myProject.Module = function() {};
myProject.Module.prototype.init = function(){
    this.foo = new myProject.Foo();
    this.bar = new myProject.Bar();
};
```
```js
// main.js
var Module = myProject.Module;
var module = new Module();
module.init();
```
参考：JavaScriptパターン

##[AMD](https://github.com/amdjs/amdjs-api/wiki/AMD)

* ビルドすることなく開発できる

```js
// module.js
define(["foo", "bar"], function(Foo, Bar){
 var foo;
 var bar;
 function Module() {}
 Module.prototype.init = function(){
   foo = new Foo();
   bar = new Bar();
 }
});
```
```js
// main.js
var Module = require("module");
var module = new Module();
module.init();
```

###ブラウザJSでの利用法
* [RequireJS](http://requirejs.org/) (scriptタグで読み込み)

##CommonJS（[Modules/1.0](http://wiki.commonjs.org/wiki/Modules/1.0)）
* ビルド前提
* [Node.js](https://nodejs.org/)で採用されているサーバサイドJS標準仕様

```js
//module.js
var Foo = require("foo");// foo.js
var Bar = require("bar");// bar.js
var foo, bar;
function ModuleCls(){}
ModuleCls.prototype.init = function(Foo, Bar){
 foo = new Foo();
 bar = new Bar(); 
}
module.exports = ModuleCls
```

```js
// main.js
var Module = require("module.js");
var module = new Module();
module.init();
```

###ブラウザJSでの利用法
* [npm](https://nodejs.org/) + [Browserify](http://browserify.org/)
* [npm](https://nodejs.org/) + [webpack](http://webpack.github.io/)

参考：[exports と module.exports の違い](http://qiita.com/kuboon/items/cb1b532ac8a8959973b2)

## [UMD(Universal Module Definition)](https://github.com/umdjs/umd)

 
[AMDとCommonJSを考える](http://blog.oimou.com/7cbfae8d/) より引用
*モジュール定義のスタイルを統一的に書けるようにしようという試みが、UMD。
UMDでは、AMDスタイル, CommonJSスタイル, そしてグローバル汚染スタイル（勝手に命名）のどの読み込み方式からでもモジュールを使えるようなモジュール定義方法をまとめている。*

```js
// some-umd-module.js

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        // AMD. Register as an anonymous module.
        define([], factory);
    } else if (typeof exports === 'object') {
        // Node. Does not work with strict CommonJS, but
        // only CommonJS-like environments that support module.exports,
        // like Node.
        module.exports = factory();
    } else {
        // Browser globals (root is window)
        root.someUMDModule = factory();
  }
}(this, function () {
    return "hello world";
}));
```


### ブラウザJS + CommonJS
* [最近の行儀のよい JavaScript の書き方](http://qiita.com/kaiinui/items/22a75d2adc56a40da7b7)より引用


```js

(function(global) {
    "use strict;"

    // Class ------------------------------------------------
    function YourModule() {
    };

    // Header -----------------------------------------------
    YourModule["prototype"]["method"] = YourModule_method; // YourModule#method(someArg:any):void

    // Implementation ---------------------------------------
    function YourModule_method(someArg) {
        // ...
    }

    // Exports ----------------------------------------------
    if ("process" in global) {
        module["exports"] = YourModule;
    }
    global["YourModule"] = YourModule;

})((this || 0).self || global);


```




### ブラウザJS + AMD + CommonJS
* [JavaScriptのモジュールの書き方](http://qiita.com/yd_niku/items/7fe3a83e7484199f46a0)より引用
* [Promiseライブラリ Q](https://github.com/kriskowal/q)の実装
* jQueryに似た書き方

```js
(function(definition){// 定義する関数を引数にとる
    // ロードされた文脈に応じてエクスポート方法を変える

    // CommonJS
    if (typeof exports === "object") {
        module.exports = definition();

    // RequireJS
    } else if (typeof define === "function" && define.amd) {
        define(definition);

    // <script>
    } else {
        MyModule = definition();
    }

})(function(){// 実際の定義を行う関数
    'use strict';

    var MyModule = function MyModule(){};

    MyModule.prototype = {
        //...
    }

    // モジュールのエクスポート
    return MyModule;
});

```

参考：[小さく始める isomorphic module pattern](http://qiita.com/Jxck_/items/14bbb49d1fd657f03343)

##ES6 Modules
* 対応ブラウザはまだない[[参考](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#Browser_compatibility)]
* ES6仕様の中でも実装されるのが遅くなるとのこと

```js
//module.js
var foo;
var bar;

export var init = function(Foo, Bar){
 foo = new Foo();
 bar = new Bar();
}
```

```js
import module from "module";
module.init();
```
参考：[Modules · Babel](https://babeljs.io/docs/usage/modules/)

#クラス

##プロトタイプパターン
* ES5未満時代の定番

```js
var MyClass = function(){
  this.id = 0;
  this.name = "test name";
};
MyClass.prototype.foo = function() {
    return this.id;
};
MyClass.prototype.bar = function() {
    return this.name;
};
var myClass = new MyClass();
myClass.foo();
myClass.bar();

//継承(JavaScriptパターン P132)
var inherit = (function() {
    var F = function () {};
    return function (C, P) {
        F.prototype = P.prototype;
        C.prototype = new F();
        C.uber = P.prototype;
        C.prototype.constructor = C;
    };
}());
var OtherClass = function(){
    this.id = 1;
    this.name = "test other";
};
inherit(OtherClass, MyClass);
var otherClass = new OtherClass();
otherClass.foo();
otherClass.bar();

```
参考：JavaScriptパターン

##モジュールパターン
* プロトタイプパターンより簡潔だが、欠点以下２つ
 * 継承先オブジェクトが継承元コピーを持つためメモリ使用量が増える
 * プロトタイプチェーンを使わずsuper関数相当の取り扱いに難がある

```js
var MyClass = function(){
  var id = 0;
  var name = "test name";
  return {
    id: {
       set: function(_id) {
          id = _id;
       }
    },
    name: {
       set: function(_name) {
          name = _name;
       }
    },
    foo: function() {
      return id;
    },
    bar: function() {
      return name;
    }
  };
};
var myClass = MyClass();
myClass.foo();
myClass.bar();

//継承（関数型継承）
var OtherClass = function() {
  var cls = MyClass();
  cls.id.set(1);
  cls.name.set("test other");
  return cls;
}
var otherClass = OtherClass();
otherClass.foo();
otherClass.bar();

```
参考：テスト駆動JavaScript

##[Object.create](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Object/create)（ECMAScript 5〜）
* IE9以上
 
```js
var myClass =Object.create({}, {
    id:{
        writable:true,
        configurable:true,
        enumerable:true,
        value: 0
    },
    name:{
        writable:true,
        configurable:true,
        enumerable:true,
        value: "test name"
    },
    foo:{
        configurable:true,
        enumerable:true,
        get: function(){
            return this.id
        }
    },
    bar:{
        configurable:true,
        enumerable:true,
        get: function(){
            return this.name
        }
    }
});
myClass.foo;
myClass.bar;

//継承
otherClass = Object.create(myClass, {
    id:{
        value: 1
    },
    name:{
        value: "test other"
    }
});
otherClass.foo;
otherClass.bar;
```
参考：テスト駆動JavaScript

##ECMAScript 6 Class
* 主にトランスパイラでビルドして使う
* Chromeで`#enable-javascript-harmony`フラグを有効にすると使えるようになる


```js
"use strict";
class MyClass {
    constructor() {
        this.id = 0;
        this.name = "test name";
    }
    foo() {
        return this.id;
    }
    bar() {
        return this.name;
    }
}
var myClass = new MyClass();
myClass.foo();
myClass.bar();

//継承
class OtherClass extends MyClass {
    constructor() {
        super();
        this.id = 1;
        this.name = "test other";
    }
}
var otherClass = new OtherClass();
otherClass.foo();
otherClass.bar();

```
参考：[Learn ES2015 · Babel](https://babeljs.io/docs/learn-es2015/#classes)

#変換（トランスパイラ・ポリフィル等）

## [Babel](http://babeljs.io/) : ES6→ES5 
* 定番トランスパイラ
* ES6 Class・Module共対応

## CommonJS→AMD
* Knockoutの実装  
[RequireJS : Simplified CommonJS wrapper について](http://blog.livedoor.jp/aki_mana/archives/8272873.html)より引用  
*ちなみに、Knockout のAMD対策はこうなってた。*

```js
define(['export', 'require'], function(koExports, amdRequire){

// koExports.* にオブジェクト定義しまくる実装。
// 返り値はない。

});
```
* [RequireJS : COMMONJS NOTES : MANUAL CONVERSION](http://requirejs.org/docs/commonjs.html)
 
```js
define(function(require, exports, module) {
    //Put traditional CommonJS module content here
});
```
#まとめ
モジュールの今の主流はCommonJS + Browserifyでしょうか。
クラスは今までのものが無駄に複雑すぎるので
トランスパイラでES6クラスを利用していきたいところ。

#参考
[JavaScriptのモジュールシステムの歴史と現状](http://qiita.com/mizchi/items/6b569cc75dbcc26a1f15)