はじめに
このライブラリはjavascriptでクラス定義がどうのこうのってハマってしまった人の指標になればと思います。
本当にこの話題はいろんなところであがっていますよね。でもどれもスッキリしないですよねぇ。
今回はjavascriptの癖のあるオブジェクト指向を、javaだとかのいままでの知識でなるべく楽したい人にオススメです。
javascriptにはクラスなんてないあるのはオブジェクトだけとか。なにもわざわざクラスを作らなくてもいいとか。こだわる人にはすすめません。
以前node.js用に作ってみたクラス定義モジュールをクライアントサイドでも使えるようにしてみました。
とはいっても、グローバル汚染対策とnodeで使用していたutil.inherits
を定義し直しただけです。
以前投稿したプロパティ、メソッド、継承、Getter/Setterの落とし穴を簡単に回避するようになっています。
難しい事を考えずにクラス定義を効率よくするためにこれからもバージョンアップしていきたいですね。
以前の投稿から、メソッドのオーバーロード、継承、prototypeへの定義の機能を追加しました。
こんなことできます
使用方法はREADME.mdにまとめてありますが、(自分で書いておきながら)読むのが面倒なので、簡単に紹介します
基本系
使用するファイルはclient.jsだけです。
とりあえず、ネームスペースCocotte
に定義関数classDefine
が設定されています。
そこらへんは自分の好きなように変更しちゃってください。
var Klass = function Klass() {
this.def(Klass);
};
Cocotte.classDefine(Klass);
Klass.properties.プロパティ名 = 定義
Klass.setMethod(メソッド名, 定義);
なんか統一感ないなと思われるかもしれません、、、実は
プロパティを定義する方法は、他にもKlass.setProperty(プロパティ名, 定義)
が存在しますが、推奨はKlass.properties
で。
同じく、メソッドを定義する方法は他にもKlass.methods.メソッド名 = 定義
が存在しますが、推奨はKlass.setMethod
で。
一応理由があるんですが、面倒なので省略します。
GitのREADME.mdにて確認ください
プロパティの型を簡単に設定したい
Klass.properties.name = {type: String};
違う型を設定すると例外が発生します
プロパティを独自にいろいろ定義したい
Klass.properties.birthday = function (pv) {
return {
// 入力値を先に変換したり
exchange: {
from: String,
to: function (val) {
return new Date(val);
}
},
// 型指定したり
type: Date,
setter: function (val) {
// 設定時にプライベート変数に格納したり
pv.birthday = val;
},
getter: function () {
// プライベート変数から取得したり
return pv.birthday;
}
};
};
入力値によって、処理をかえたい時に便利です。
むしろjavaではgetter/setterの実装が遅れているので、こっちのほうが便利かも。
メソッドの引数の型をチェックしたい
Klass.setMethod('setHolidays', {
params: [String, [Date]],
method: function (name, holidays) {
this.holidays[name] = holidays;
}
});
配列の場合は要素の型も確認できるようになっています
メソッドをオーバーロードさせたい
同じメソッド名で登録するとオーバーロードになります
Klass.setMethod('setName', {
params: [String],
method: function (val) {
this.name = val;
}
});
Klass.setMethod('setName', {
params: [Number],
method: function (val) {
this.setName('A' + val);
}
});
継承を簡単に実現したい
// 親クラス
var SuperKlass = function SuperKlass (prop1) {
this.def(SuperKlass);
this.prop1 = prop1;
};
Cocotte.classDefine(SuperKlass);
SuperKlass.properties.prop1 = {type: String};
// サブクラス
var Klass = function Klass (prop1, prop2) {
this.def(Klass, prop1);
this.prop2 = prop2;
};
Cocotte.classDefine(Klass, SuperKlass);
Klass.properties.prop2 = {type: String};
// インスタンス
var k = new Klass('foo', 'bar');
console.log(k.prop1); // 'foo'
普通のprototypeの定義とも同居できるよ
次のようにいつもの定義とsetMethod
で定義したメソッドは同居できます。
Klass.prototype.getPrice = function () {
return Math.floor(price * 1.08);
};
ただし、同名のときは後に定義したもので、上書きされます
クラスの使い方も変わらないよ
var instance = new Klass('foo', 123);
instance.name = 123; // 一番最初の例だとここで例外発生
instance.setName(123); // オーバーロードの例だとname='A123'
最後に
まだまだ実装できていない機能はたくさんありますけど、そこはやったりやらなかったり追々きめようかな。
インターフェースとか、privateメソッドとか、オーバーライド禁止、抽象クラスとか
javascriptはそのままなんにも考えずに使うと本当に落とし穴がおおくて、無駄に時間を消費してしまいます。
すこしでもお役に立てれば幸いです。またライセンスはMITですので後はご自由に。