Protovisを読んでJavaScriptを知る
pv.Layout.Forceを改造したクラスを作るために、pv.Layout.Forceをコピーし、NewForceとした。
このNewForceクラスのnodesプロパティの代わりに新しいプロパティclustersを追加することにした。
nodesプロパティは、Forceの親であるpv.Layout.Networkに定義されているのだが、
このnodesプロパティの定義を読んで感心することしかり。
現在のJavaScriptの真髄が隠されている、といっても過言ではないと思う。
これがnodesのプロパティ定義。これはいったい何をしているのか。
pv.Layout.Network.prototype = pv.extend(pv.Layout)
.property(“nodes”, function(v) {
return v.map(function(d, i) {
if (typeof d != “object”) d = {nodeValue: d};
d.index = i;
d.linkDegree = 0;
return d;
});
})
Protovis名前空間の関数、pv.extendsは、Protovisの提供するクラスシステムの継承を行う関数。
引数として指定したプロトタイプを持つ別のオブジェクトを返すようになっている。
また、.property(name, cast)は、表現クラスMarkのプロトタイプ関数、pv.Mark.prototype.property(name, cast)である。
nameで指定したプロパティとして、castで指定したgetter-setterを追加する。
このプロパティは、使用時のMarkを継承した型のプロトタイプの「properties」配列に追加される。
pv.Mark.prototype.propertyでは、呼び手のプロトタイプにpropertiesという配列変数をつくり、nameを添字としたスロットをtrueにしている。こうすることで、プロパティの有無を確認できる。
そして、振る舞いを表す関数castをpv.Mark.prototype.propertyMethodに渡す。
pv.Mark.prototype.propertyMethodでは、castをgetter-setterを表す関数として設定する。
.property(“nodes”, function(v) {
のvはsetterとして振舞ったときの引数として書く。
getter-setterをひとつの関数として表すのは、Javaなどを使っている人から見ると奇異に感じるだろう(僕もだ)。
pv.Mark.prototype.propertyMethodは、castが呼ばれたときの引数の数をチェックし、有ればセットしようとする(もう少し複雑だが)ように、プロトタイプに定義を行う。
次に登場するのは、JavaScript1.6から登場した、配列のmap関数である。
Mozzila Developer Network https://developer.mozilla.org/ja/Core_JavaScript_1.5_Reference/Global_Objects/Array/map
var mappedArray = array.map(callback[, thisObject]);
callback:現在の配列から新しい配列の要素を生み出すための関数を記述する。
thisObject:callback関数内で「this」で使用するオブジェクトを指定できる。
また、callbackに指定した関数の引数は、
要素の値、要素のインデックス、操作されているArrayオブジェクト
である。
すると、nodes配列の要素の定義は以下のように読むことができる。
// 配列の1要素とインデックスを引数にとる。
return v.map(function(d, i) {
// 要素がオブジェクトでなければ、値「nodeValue」プロパティとしたオブジェクトにする。
if (typeof d != “object”) d = {nodeValue: d};
// ノードのプロパティindexを配列のインデックスにする。
d.index = i;
// リンク度合いを0にする
d.linkDegree = 0;
// 新しい要素
return d;
});
配列の各要素を再定義するメソッドを配列に定義するなんて発想は、クラス中心のプログラミングでは考えもしなかったが、
JSONとして知られる、JavaScriptのオブジェクトリテラルで渡された配列を初期化したり、整理したりするには都合が良さそうだ。
もしもこれをJavaで書くならば、ArrayListを継承するなり包含するなりして、
オーバライドしたaddメソッドやsetメソッドに処理を記述するのが近いだろうか。
こうしたコードを読むと、JavaScriptでは関数が第1級オブジェクトであることを思い知らされる。
結局、プロパティclustersを追加するには、このnodesに倣って初期化を行うのがよさそうだ。
(ここに書かれたコードは、全角スペースでインデントしてあります。全角スペース2個をタブとして置換してください)
By i-matsunami, 2011/03/03 @ 11:04 PM
素晴らしい解説。やっと仕組みがわかりました。
あとは、svgの描画速度をなんとかしてほしいですね。
By shuji, 2011/03/04 @ 1:58 PM
コメントありがとうございます。
知れば知るほど奥が深いです。
SVGの描画速度は・・・;
先日、forceのデモをiPhone4で見てみたら、お湯を沸かして珈琲を落とすくらいの時間がかかりますね;
描画頻度を落とせば良いのかも知れません。