ES 5thなクラスを生成3

前回はFunctionオブジェクトの拡張を試してみたのだけど、ネイティブオブジェクトに独自メソッドぶら下げるの気持ち悪いという意見もあるので、クラスを作るオブジェクトを作成してみる。

var Class = (function(){
    var mixin = function(trait) {
        Object.defineProperties(this.prototype, trait);
        return this;
    };
    var create = function(){
        var obj = Object.create(this.prototype);
        if (typeof(obj.init) == "function") { obj.init.apply(obj, arguments); }
        return obj;
    };
    return Object.create(null, {
        define: {
            value: function(descriptor, superClass){
                var c = function(){};
                if (superClass) { c.prototype = Object.create(superClass.prototype) }
                if (descriptor) { Object.defineProperties(c.prototype, descriptor) }
                c.create = create;
                c.mixin = mixin;
                return c;
            }
        },
        inherit: {
            value: function(superClass) {
                var obj = {
                    define: function(desc){ return Class.define(desc, superClass) }
                };
                return obj;
            }
        }
    });
})();

Classというユーティリティを作成してみた。クラス定義ついでに継承とミックスインも実装。

// クラス定義
var Hoge = Class.define({
    hoge: { get: function(){return this._a} },
    init: { value: function(a){ this._a = a; }},
    toString: { value: function(){ return String(this._a) }},
});

// オブジェクト生成
var h = Hoge.create("a");
console.log(h.hoge)

Class.defineにプロパティディスクリプタを渡すとクラスになる。JavaScriptなのにクラスベースっぽく見える

// 継承してクラス作成
var Fuga = Class.inherit(Hoge).define({
    init: { value: function(a,b) { Hoge.prototype.init.call(this, a); this._b = b }},
    fuga: { get: function(){ return this._b }},
    toString: { value: function(){ return String(this._a) + String(this._b) }},
});

// オブジェクト生成
var f= Fuga.create("a", "b");
console.log(f.fuga)
console.log(f.hoge)

継承は Class.inherit(SuperClass).define({ ... });

// Trait(というかただのプロパティディスクリプタ)
var Piyo = {
    c: {
        get: function() { return this._c; },
        set : function(c) { if (isNaN(c)) { throw new Error("Not a Number"); } this._c = c; }
    }
};

// ミックスイン
Hoge.mixin(Piyo);

// オブジェクト生成
var h2 = Hoge.create("hoge");
h2.c = "あ"  // -> 例外スロー "Not a Number"

ミックスインは Class.inherit(SuperClass).define({ ... }).mixin(Trait1).mixin(Trait2) 。