LoginSignup
92
92

More than 5 years have passed since last update.

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

Last updated at Posted at 2015-07-31

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

モジュール

ブラウザJS(モジュールなし)

ES5以前にモジュールという仕様はないので
グローバルオブジェクト作成し
名前空間パターン
var obj = obj || {}

JSファイルの分割結合をできるように。

// foo.js
var myProject = myProject || {};
myProject.Foo = function(){};
// bar.js
var myProject = myProject || {};
myProject.Bar = function(){};
// module.js
var myProject = myProject || {};
myProject.Module = function() {};
myProject.Module.prototype.init = function(){
    this.foo = new myProject.Foo();
    this.bar = new myProject.Bar();
};
// main.js
var Module = myProject.Module;
var module = new Module();
module.init();

参考:JavaScriptパターン

AMD

  • ビルドすることなく開発できる
// module.js
define(["foo", "bar"], function(Foo, Bar){
 var foo;
 var bar;
 function Module() {}
 Module.prototype.init = function(){
   foo = new Foo();
   bar = new Bar();
 }
});
// main.js
var Module = require("module");
var module = new Module();
module.init();

ブラウザJSでの利用法

CommonJS(Modules/1.0

  • ビルド前提
  • Node.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
// main.js
var Module = require("module.js");
var module = new Module();
module.init();

ブラウザJSでの利用法

参考:exports と module.exports の違い

UMD(Universal Module Definition)

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

// 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


(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

(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

ES6 Modules

  • 対応ブラウザはまだない[参考]
  • ES6仕様の中でも実装されるのが遅くなるとのこと
//module.js
var foo;
var bar;

export var init = function(Foo, Bar){
 foo = new Foo();
 bar = new Bar();
}
import module from "module";
module.init();

参考:Modules · Babel

クラス

プロトタイプパターン

  • ES5未満時代の定番
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パターン

モジュールパターン

  • プロトタイプパターンより簡潔だが、欠点以下2つ
    • 継承先オブジェクトが継承元コピーを持つためメモリ使用量が増える
    • プロトタイプチェーンを使わずsuper関数相当の取り扱いに難がある
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(ECMAScript 5〜)

  • IE9以上
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フラグを有効にすると使えるようになる
"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

変換(トランスパイラ・ポリフィル等)

Babel : ES6→ES5

  • 定番トランスパイラ
  • ES6 Class・Module共対応

CommonJS→AMD

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

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

});
define(function(require, exports, module) {
    //Put traditional CommonJS module content here
});

まとめ

モジュールの今の主流はCommonJS + Browserifyでしょうか。
クラスは今までのものが無駄に複雑すぎるので
トランスパイラでES6クラスを利用していきたいところ。

参考

JavaScriptのモジュールシステムの歴史と現状

92
92
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
92
92