コーディングスタイル解説
せつこそれ抽象化やない複雑化や
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 が入ります。これで共有ができました。
もうだいたいいいですよね。疲れてしまいました。途中から丁寧語になった気がします。