ログインしてさらにmixiを楽しもう

コメントを投稿して情報交換!
更新通知を受け取って、最新情報をゲット!

プログラミング王国コミュのJavaScript

  • mixiチェック
  • このエントリーをはてなブックマークに追加
最近いろいろ勉強してた割にはまだ一度もここに書いてなかった。

オブジェクト指向的なJavaScriptをそれなりにかけるようになってきたので、
クラス定義用のメソッドを紹介。

といっても某書籍のほぼコピペ。

一部バグ?ってたので修正したり、コメントを自分なりに分かりやすくつけてます。

フォルダ構造は

ルートディレクトリ
┗sample.html(JavaScript起動用)
┗js
 ┗class.js(クラス定義用)
 ┗test.js(class.jsを利用)

って感じです。

test.js は、継承・Mixin、ダックタイピングの例です。

クロージャーもなかなか面白い。
クロージャーを使った際のスコープとかなんだか不思議な感じ。
ブックマークレットとかもかなり興味深いです(*ノノ)
正直ブックマークレットは反則技だと思う(ノ´∀`*)
イベントハンドラをブックマークレットで追加したり削除したり、何でもありかよ!?
みたいな・・
時間があったらいろいろ紹介。

コメント(6)

<!-- sample.html -->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<HTML>
<HEAD>
<META http-equiv="Content-Type" content="text/html; charset=Shift_JIS">
<script type="text/javascript" src="js/class.js"></script>
<script type="text/javascript" src="js/test.js"></script>
<TITLE></TITLE>
</HEAD>
<BODY>


</BODY>
</HTML>
/**
* class.js
* クラス定義を行うフレームワークを提供
*/

//名前空間用オブジェクト生成
if (typeof (js) == "undefined") js = {};
if (typeof (js.tomaton) == "undefined") js.tomaton = {};
if (typeof (js.tomaton.Class) == "undefined" ) js.tomaton.Class = {};

//無名関数内でクラス定義
//いちいち名前空間付で定義するのはめんどいので最後に名前空間に登録する。
(function () {
var define = function(data) {
//クラス名
var classname = data.name;
//スーパークラス(継承するクラス)
var superclass = data.extend || Object;
//コンストラクタ
var constructor = data.construct || function(){};
//メソッドを定義したオブジェクト
var methods = data.methods || {};
//静的変数、メソッドを定義したオブジェクト
var statics = data.statics || {};
//Mixin用クラス(or その配列)
var borrows;
//ダックタイピング検証用クラス(or その配列)
var provides;

//Mixin用クラスをローカル変数に格納
//単体・配列に対応
if (!data.borrows) borrows = [];
else if (data.borrows instanceof Array) borrows = data.borrows;
else borrows = [data.borrows];

//ダックタイピング検証用クラスをローカル変数に格納
//単体・配列に対応
if (!data.provides) provides = [];
else if (data.provides instanceof Array) provides = data.provides;
else provides = [data.provides];

//プロトタイプの元となるオブジェクトを生成
//スーパークラスを指定した場合はスーパークラスのprototypeを引き継ぐ
var proto = new superclass();

//継承されたプロパティ以外は削除する
for (var p in proto)
if (proto.hasOwnProperty(p)) delete proto[p];

//Mixinクラスからメソッドをプロトタイプにコピー
for (var i = 0;i < borrows.length;i++) {
var c = data.borrows[i];
borrows[i];
//メソッドを、cのプロトタイプから新しいクラスのプロトタイプにコピー。
for (var p in c.prototype) {
if (typeof c.prototype[p] != "function") continue;
proto[p] = c.prototype[p];
}
}

//インスタンスメソッドをプロトタイプオブジェクトにコピーする
//Mixinメソッドを上書きする場合もある。
for (var p in methods) proto[p] = methods[p];

//予約語として、constructor, superclass, classnameを定義
//コンストラクタ
proto.constructor = constructor;
//スーパークラス
proto.superclass = superclass;
//クラス名(指定のある場合)
if (classname) proto.classname = classname;

//ダックタイピング検証
//想定したメソッドが全て存在するかどうかチェックする
for (var i = 0;i < provides.length;i++) {
var c = provides[i];
for (var p in c.prototype) {
if (typeof c.prototype[p] != "function") continue;
if (p == "constructor" || p == "superclass") continue;
//同じ名前のメソッドを持ち、引数の数が同じかどうかチェックする
//同じ場合は処理続行
if (p in proto &&

typeof proto[p] == "function"
&& proto[p].length == c.prototype[p].length) continue;
//異なる場合は例外をスロー
throw new Error("Class " + classname + " dose not provide method "
+ c.prototype.classname + "." + p);
}
}

//プロトタイプオブジェクトをプロトタイプに設定
constructor.prototype = proto;

//静的なプロパティをコンストラクタにコピー
for (var p in statics) constructor[p] = data.statics[p];

//コンストラクタ関数を返す。
return constructor;
};

// 名前空間をローカル変数に代入
var ns = js.tomaton.Class;
ns.define = define;
})()
/**
* ユーザ用JS
* (フレームワークを利用する側)
*/

//クラス定義を無名関数内で行う
(function() {
//クラス定義用メソッドをグローバル変数に代入
var define = js.tomaton.Class.define;

/**
* スーパークラス(テスト用)
*/
SuperClass = define(
{
name : "SuperClass",
construct : function(a) {this.a = a;},
methods : {
superMethod : function() {alert(this.a);}
}
});

/**
* サブクラス(テスト用)
*/
SubClass = define(
{
name : "SuperClass",
extend : SuperClass,
construct : function(a, b) {
this.superclass(a);
this.b = b;
},
methods : {
subMethod : function() {alert(this.a + ":" + this.b);}
}
});


/**
* とりさんクラス
*/
Bard = define(
{
name : "Bard",
construct : function() {},
methods : {
poke : function() {alert("つんつん");}
}
});

/**
* あひるたんクラス
*/
Duck = define(
{
name : "Duck",
extend : Bard,
construct : function() {
},
methods : {
/**
* あひるたんは歩くの
*/
walk : function() {alert("てくてく");},
/**
* あひるたんは鳴くの
*/
quack : function() {alert("ガァガァ");}
}
});

/**
* Mixin用クラスその1
*/
Mixin1 = define(
{
name : "Mixin1",
construct : function() {
},
/**
* 歩くメソッドを提供する
*/
methods : {
walk : function() {alert("てくてく");}
}
});

/**
* Mixin用クラスその2
*/
Mixin2 = define(
{
name : "Mixin2",
construct : function() {
},
/**
* 鳴くメソッドを提供する
*/
methods : {
quack : function() {alert("ガァガァ");}
}
});

/**
* あひるたんのようなクラス
*/
LikeDuck = define(
{
name : "LikeDuck",
construct : function() {
},
methods : {
poke : function() {alert("ぷぎゃーーー!!")}
},
/**
* 歩くメソッドと鳴くメソッドをMixinクラスから借用する
*/
borrows : [Mixin1, Mixin2],
provides : [Duck]

});

try {
/**
* あひるたんのようなクラス
*/
NotLikeDuck = define(
{
name : "NotLikeDuck",
construct : function() {
},
methods : {
/**
* あひるたんは飛べない!>_<。
*/
fly : function() {alert("⊂二二二( ^ω^)二⊃")}
},
/**
* 歩くメソッドと鳴くメソッドをMixinクラスから借用する
*/
borrows : [Mixin1, Mixin2],
provides : [Duck]

});
} catch (e) {
alert(e);
}
js.SuperClass = SuperClass;
js.SubClass = SubClass;
js.Bard = Bard;
js.Duck = Duck;
js.LikeDuck = LikeDuck;
})();


//定義したサブクラスを利用する
var b = new js.SubClass("SuperClass", "SubClass");
//スーパークラスのメソッド
b.superMethod();
//サブクラスのメソッド
b.subMethod();

//あひるさんのようなクラスを使ってみる
var likeDuck = new js.LikeDuck();
//あひるさんのように鳴く
likeDuck.walk();
//あひるさんのようにあるく
likeDuck.quack();
//あひるさんのようにつつく?
likeDuck.poke();
めんどうだったので、alertで表示しちゃってます。
あと、エラーメッセージがIEだとでなかった。
ちゃんとエラーメッセージ取り出して表示してあげないと駄目なのか・・・

catch節の以下の行

alert(e);

を

var msg = "";
msg += "name : " + e.name + "\n";
msg += "message : " + e.message + "\n";
msg += "fileName : " + e.fileName + "\n";
msg += "lineNumber : " + e.lineNumber + "\n";
msg += "stack : " + e.stack + "\n";
alert(msg);

に置き換えれば詳細なエラーメッセージがIEで見れます。

あと、自分がはまったところ。

class.js の44行目あたりに、

var proto = new superclass();

とあるが、
コンストラクタ内で例外が発生するようなことがあるとこの行で処理が中断されてしまう。

これは、引数に渡したsuperclass をここで呼び出しているため、コンストラクタでエラーが発生すると例外がスローされてしまう為です。

この回避策としては、コンストラクタで引数なしのときに例外を発生させないようにすることです。

例えば、


var extend = function(hoge) {
if (arguments.length != 0) {
if (hoge == null) throw new Error("hoge is empty.");
}
}

SuperClass = define(
{
name : "name",
"extend" : extend,
method : {hoge : function(){};}
});

こんな感じ。
これなら、引数なしで
superclass()
みたいな呼び出しをされても例外はスローされず、
実際にnew したときの引数がnullのときに例外がスローされる。

ただ、これだと引数が与えられてない場合にエラーとしたい場合にできないが
そんなことしない!!
これだ(*ノノ)

その辺(コンストラクタの引数など)はしっかりコメントで記述し、コーディングルールでも作っておけばいいだろう。
たぶん。

ログインすると、みんなのコメントがもっと見れるよ

mixiユーザー
ログインしてコメントしよう!

プログラミング王国 更新情報

プログラミング王国のメンバーはこんなコミュニティにも参加しています

星印の数は、共通して参加しているメンバーが多いほど増えます。