コーディングスタイル解説

せつこそれ抽象化やない複雑化や

8Kは福祉 on Twitter: "せつこそれ抽象化やない複雑化や"

dom.js · GitHub を元に解説する。

var dom = exports;
(function() {
	dom.Node = Node;
	function Node() {}
	// 略
})();

基本的に名前空間はひとつ (ここでは dom) しか汚さない。exports は node.js の方言。普通は this とか window とかになる。あとは匿名関数の中に書いて、dom.Node = Node; などしてオブジェクトを拡張していく。dom.Node = Node; が function Node() {} より先にあるのに Node を参照できるのはどうして?と思われるかもしれませんが、関数宣言は先に評価されるので、大丈夫です。var Node = function() {}; だとダメですが。何となく dom.Node = Node; が先のほうが「ここでオブジェクトを拡張している」ことが伝わりやすいかと思ったのです。それだけです。どっちでもいいよ。

	function extend(d, s) { for (var p in s) d[p] = s[p]; }

継承のために使う extend 関数。よく見かけるコピペ lib ですね。手抜きで戻り値なし。

	function Node() {}
	// 略
	Element.prototype = new Node();

Node クラス (呼び方クラスでいいですか、めんどくさいんで) のコンストラクタ。何もしない。Element クラスは Node を継承するので、Element.prototype = new Node(); する。これで new Element() instanceof Node が true になる。えっ、それだけです。こうしておかないと、いつか困るかもしれないじゃないですか。

	Node.__defineGetter__('ELEMENT_NODE'               , function() { return  1; });
	// 略

Node クラスの定数ですね。外部からも使いたいので、Node のプロパティにしますが、readonly にしたいので、__defineGetter__ だけです。Object.defineProperty が使えるなら、そのほうがいいかも。どちらもない場合は諦めましょう。

	Node.create = function(spec, my) {
	// 略
	};

コンストラクタを何もしないことにしたので、create メソッドを定義して頑張ることにします。spec, my は Javascript: The Good Parts を読んだ人ならご存知のアレです。spec はインスタンス生成に必要な諸々を入れたオブジェクト、my は共有オブジェクトです。「共有? 誰と誰が?」の件につきましては、「Node と Element で共有するが、外部からは使わせないオブジェクト」くらいの解釈でいいと思います。でも実は Node.create の引数にしてあるので、var obj = {}; Node.create(spec, obj); などとしてやれば、obj から参照できるという保険付き。

		my = my || {};

my は引数で渡さないことが多いです。渡されなかったときのことを考えて、空オブジェクトを用意しておきます。

		var that = new Node();

that がでてきました。これが create メソッドの戻り値オブジェクトです。これに public なプロパティやメソッドを入れます。Node のインスタンスなので new Node(); で作ることにします。これで that instanceof Node が true です。

		var _nodeName       = spec.nodeName;
		that.nodeValue      = spec.nodeValue;
		var _nodeType       = spec.nodeType;
		that._parentNode    = null;
		that._childNodes    = [];
		that._ownerDocument = null;

var 宣言は private 変数、that のアンダースコアなしプロパティは public 変数、アンダースコア付きはなんちゃって private 変数です。ちょっと隠しきれませんでした。すいません。とりあえず spec 引数で渡された値をコピーしたり、初期値なんかを入れておきます。

		that.__defineGetter__('nodeName'     , function() { return _nodeName; });
		that.__defineGetter__('nodeType'     , function() { return _nodeType; });
		that.__defineGetter__('parentNode'   , function() { return that._parentNode; });
		that.__defineGetter__('childNodes'   , function() { return that._childNodes; });
		that.__defineGetter__('ownerDocument', function() { return that._ownerDocument; });

var 宣言は private 変数というのは少し嘘でした。外部から値の参照はします。__defineGetter__ の出番です。なんちゃって private 変数のほうは Getter だけですが、所詮オブジェクトを返しているだけなので、受け取り先でやりたい放題ですね……!

		that.insertBefore  = insertBefore;
		that.replaceChild  = replaceChild;
		that.removeChild   = removeChild;
		that.appendChild   = appendChild;
		that.hasChildNodes = hasChildNodes;

that を拡張していきます。後のほうで定義している関数への参照です。

		my.getElementsByTagName = function(tagName) {
		// 略
		};

my も拡張します。getElementsByTagName は Element クラスと Document クラスで使います。_childNodes などを参照したいので、Node クラスで定義しておきます。that に拡張しないのは何故かと言うと、Node は getElementsByTagName メソッドを持たないからです。持っていてもいいかもしれませんが、まあそういう仕様にした (DOM の仕様がそうなってる) ので。

		function insertBefore(newChild, refChild) {
			newChild._parentNode = that;
			that._childNodes.splice(findChildIndex(refChild), 0, newChild);
			return newChild;
		}

		function replaceChild(newChild, oldChild) {
			newChild._parentNode = that;
			that._childNodes.splice(findChildIndex(oldChild), 1, newChild);
			return newChild;
		}
		// 略

関数の列挙です。どうせクロージャの中あったかいナリなので、好きに書いたらいいんです。関数の中からは private 変数も見えるので、自由な感じです。でも var insertBefore = function() {}; な書き方にするなら that.insertBefore = insertBefore; より前に書きましょう。

		return that;

諸々拡張された that を return します。忘れそうになりますが、Node.create メソッドの戻り値です。

	dom.Element = Element;
	function Element() {}
	Element.prototype = new Node();

Element クラスです。前述のとおり、Node を継承します。

	Element.create = function(spec, my) {
		spec.nodeName  = spec.tagName;
		spec.nodeValue = null;
		spec.nodeType  = Node.ELEMENT_NODE;

		my = my || {};

		var that = new Element();
		extend(that, Node.create(spec, my));
		// 略
	};

基本的に Node のときと同じですが、Node のプロパティとメソッドを使いたいので extend しないといけません。Node.create をして (必要な spec はよしなに)、前述の諸々拡張されたオブジェクトを受け取り、自分の that を拡張します。このとき、create メソッドに自分の my を渡しているので、これには getElementsByTagName が入ります。これで共有ができました。

もうだいたいいいですよね。疲れてしまいました。途中から丁寧語になった気がします。