JavaScriptのオブジェクト指向をcrowbarから考える(その2)

昨日の続きです。
http://d.hatena.ne.jp/kmaebashi/20090621/p1

id:jdgさんの記事。
JavaScriptのオブジェクトについて考察してみた - あと味

そのコメント欄で紹介されたnanto_vi(nanto_terapad?)さんの記事。
JavaScript の new 演算子の意味: Days on the Moon

JavaScript における new 演算子の動作は大まかにいって以下のとおりである。(new F() とした場合。)

  1. 新しいオブジェクトを作る。
  2. 1.で作ったオブジェクトの Prototype 内部プロパティ (__proto__ プロパティ) に F.prototype の値を設定する。
    • F.prototype の値がオブジェクトでないのなら代わりに Object.prototype の値を設定する。
  3. F を呼び出す。このとき this の値は 1 で作ったオブジェクトとし、引数には new 演算子とともに使われた引数をそのまま用いる。
  4. 3 の返り値がオブジェクトならそれを返す。そうでなければ 1 で作ったオブジェクトを返す。

昨日の私の記事。

つまり、JavaScriptでは、newを使うことで、上のcrowbarのリストにおける、

3行目でオブジェクトを作ってthisに代入しているところを、Days on the Moonの記事の1の処理が、
17行目でthisをリターンしているところを、Days on the Moonの記事の4の処理が、
そしてcreate_point()関数を呼び出すという処理をDays on the Moonの記事の3の処理が、
それぞれ代行してくれるわけです。

一読してわかるように、Days on the Moonの記事の2番目の項目を説明していませんので、今日はここから。

(crowbarではそうではないのですが)JavaScriptでは、関数もまた「プロパティを持つことができるオブジェクト」です。
Days on the Moonの記事の2番目の項目によれば、新しいオブジェクトの__proto__プロパティに、関数オブジェクトのprototypeプロパティを設定するとのことなので、まずは試してみましょう(Firefox限定)。

function Hoge() {
}
var o = new Hoge();

if (o.__proto__ === Hoge.prototype) {
  console.log("OK!"); // Hoge.prototypeが、o.__proto__に設定されている
} else {
  console.log("NG!");
}
// 実行結果:「OK!」と表示される

IEでは__proto__プロパティを陽に操作できないだけで、内部的には同じようなことを行っています。

そして、JavaScriptは、あるオブジェクトについて、存在しないプロパティが参照されたときは、そのオブジェクトの__proto__プロパティに設定されているオブジェクトを参照に行くわけです(__proto__はFirefox限定なので、正確にはPrototype内部プロパティ)。
実験。

function Hoge() {
}
Hoge.prototype.piyo = 10;
var o = new Hoge();
console.log("o.piyo.." + o.piyo);
// 実行結果:「o.piyo..10」と表示される

o.__proto__にはHoge.prototypeが設定されているので、プロパティが存在しない場合そちらを参照に行っていることがわかります。

さて、では、なぜJavaScriptがこのような仕様になっているかですが。

昨日示したcrowbarのコードでも、オブジェクトの生成は可能ですしメソッドも付いています。継承らしきこともできました。
ただし、crowbar流のやり方では、インスタンスごとにメソッドへの参照がくっつきます。つまりPointにメソッドが10個あって、Pointのインスタンスが1000個あったら、100個のインスタンスそれぞれに100個ずつ、メソッドへの参照がくっつくわけです。これが許容できないと思う人はいるでしょう*1

その点、JavaScriptのように、「プロパティが存在しない場合__proto__を見に行く」という規則を入れておき、かつ、「オブジェクト生成の際に、関数オブジェクトのprototypeプロパティを__proto__にセットする」という規則も入れれば、「メソッド」は、オブジェクトのコンストラクタとなる関数のprototypeオブジェクトにくっつけておけばよい、ということになります。

「その2」はほとんどcrowbarと関係ないのでは、と思わなくもないですが、crowbarに興味を持った方はぜひこちらを。

*1:私は、「どうせスクリプト言語だし、別にそれでいいんじゃね?」と思ったからこうしているわけですが。