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()を使えということか。