Dojoをちょっぴり真面目に網羅的に調査 dojo.lang編(1)

dojoはドキュメントがほぼないのが最大の難点(公式wikiにちょっとはあるけど)なので、調査がてらソースを手当たりしだい読んでみる。まずはdojo.lang周りから。対象バージョンは先ほど落とした7月24日のnightly buildとする。(最新リリースの0.3.1からいくつか構造の変更があるみたいなので)

以降ソースの引用が何回か為されるが、著作権法上許可された引用であるということでヨロシク。

まずdojo.lang本体であるsrc/lang.jsを見てみるとたった3行しかない。

dojo.provide("dojo.lang");
dojo.provide("dojo.lang.Lang");

dojo.require("dojo.lang.common");

dojo.provideはJavaのパッケージ相当に見えるように模したオブジェクトを提供し、requireはその逆でパッケージをインポート(相当)のことを行う。つまりsrc/lang.jsを読み込むとdojo.langとdojo.lang.Langパッケージが提供され、中ではdojo.lang.commonをインポートしている。ということは

dojo.require("dojo.lang");
dojo.require("dojo.lang.Lang");
dojo.require("dojo.lang.common");

上の3行は等価のようだ。

一応確認してみる。dojo.langは非ビジュアルなのでRhinoで読み込むのがお手軽。

load("dojo.js");
dojo.require("dojo.lang"); // *
print(dojo.lang.extend);

dojo.jsと同じ場所に上のjsを置きRhinoに読み込ませて実行すると、requireの行が3つのうちどれであってもちゃんとextend関数の中身が表示された。

さてこの3種類のインポート方法のうち、dojo.lang.Langに関してはdeprecatedであることがsrc/lang/Lang.jsの中に記述してあるのだが、残るdojo.langとdojo.lang.common (更にいえばもう一つ、dojo.require("dojo.lang.*")という記述)のどれが推奨されるのだろう?

やっぱり短いが正義ってことでdojo.require("dojo.lang")かな。そういえばsrcフォルダ直下にはほかにもショートカットのためっぽいjsがいくつもあるし。

こんだけ書いたらもう眠くなった。まだぜんぜん中身に到達してないじゃん!(セルフ突っ込み)

dojo.lang.mixin()とdojo.lang.extend()

mixinって言語によって微妙にやってることが違ったり、定義をスッキリ解説してくれているページがないのでよくわからないけれども、dojoにおいては単純明快。

src/lang/common.js先頭より。

dojo.lang._mixin = function(/*Object*/ obj, /*Object*/ props){
	// summary:	Adds all properties and methods of props to obj.
	var tobj = {};
	for(var x in props){
		// the "tobj" condition avoid copying properties in "props"
		// inherited from Object.prototype.  For example, if obj has a custom
		// toString() method, don't overwrite it with the toString() method
		// that props inherited from Object.protoype
		if(typeof tobj[x] == "undefined" || tobj[x] != props[x]) {
			obj[x] = props[x];
		}
	}
	// IE doesn't recognize custom toStrings in for..in
	if(dojo.render.html.ie && dojo.lang.isFunction(props["toString"]) && props["toString"] != obj["toString"]) {
		obj.toString = props.toString;
	}
	return obj;
}

objにpropsを全コピー。わかりやすいが、最初パッと見たときはtobjが何をしているのか分からなかった(使ってないし)。コメントの通りObjectのprototypeが拡張されていたときに(その拡張プロパティが変更されていない限り)避けてコピーするためのようだ。

_で始まるプロパティはprivate扱いというお約束*1なので、実際にはこっちを使う。

dojo.lang.mixin = function(/*Object*/ obj, /*Object...*/ props) {...}

propsはいくつでもよいと。

続いてextend。

dojo.lang.extend = function(/*Object*/ constructor, /*Object...*/ props){
	// summary:	Adds all properties and methods of props to constructors prototype,
	//			making them available to all instances created with constructor.
	for(var i=1, l=arguments.length; i<l; i++){
		dojo.lang._mixin(constructor.prototype, arguments[i]);
	}
	return constructor;
}

コンストラクタのprototypeにmixinしているだけ。拡張したいのがインスタンスの時mixin()、クラス(コンストラクタ)のときextend()を使えということか。