«前の日記(2008年01月20日) 最新 次の日記(2008年01月22日)» 編集

ema log


2008年01月21日 [長年日記]

_ [JavaScript] prototype と __proto__ と constructor

いまいち働いてない頭であれこれ考えて、手を動かしてみた。こんなことばっかりやっててすみません。ここ一週間ほどのまとめです。

Kanasan.JS JavaScript 第5版読書会 #2 雑感」 [blanket sky] に同様の内容が書かれていますが、うちより断然まとまってました。

まとめると

  • constructor: 初期化に使われた関数(オブジェクト)(であることが期待される、ぐらい?)
  • prototype: 拡張するためのオブジェクト(コンストラクタで初期化されたオブジェクトに対して)
  • __proto__: プロトタイプオブジェクト(プロパティが見つからなかったときに探索しに行くオブジェクト)

の様になります。色々な人が苦しんでるんだろうなぁ。


prototype と __proto__ と constructor の関係

ECMAScript3 のオブジェクトの実体は(拡張された)Hash / 連想配列 です。また、それぞれのオブジェクトは、関連した「プロトタイプオブジェクト」を持ちます。たとえば、次のようなコードを考えます。なお、__proto__ は Firefox などにおける独自拡張です。このとき、という関係になります。「プロトタイプオブジェクト」と「prototype プロパティに束縛されたオブジェクト」は別物で有ることに注意してください。ここでまず嵌りました。

Kanasan.JS JavaScript 第 5 版読書会 #2」 [Days on the Moon] を読むまで気がつきませんでした。。

以下では、__proto__ を使用して、後からチェーンをつなぎ替えていますが、。。


プロトタイプチェーン

さらに、この 3 つのプロパティは、「組み込みで用意されている特別なプロパティ」などではなく、「役割は特別」ですが、実体は「ただの普通のプロパティ」です。代入も可能でのようになります。ここで、b.a で "a" を取り出せたのが、「プロトタイプチェーン」と呼ばれる仕組みで、a, b を生成した段階では

だったのを「b.__proto__ = a;」と、チェーンをつなぎ替えたことで

  1. b のプロパティ a
  2. b の「プロトタイプオブジェクト」のプロパティ a

の順番に探索が行われた結果、"a" が取得されたわけです。少し変形してみてるとのように、b.a と a.a は同じオブジェクトを指していることが確認できます。


__proto__ と コンストラクタ の関係

new Constructor で初期化される際にに相当するような処理が内部で行われているようです。

以上のような仕組みがあるので、などと、Array コンストラクタの prototype プロパティのオブジェクトを拡張することで、Array コンストラクタで生成された全てのオブジェクトに対し、拡張を行ったのと同じ効果が得られるわけです。この仕組みを活用することで、いわゆるクラス感覚でプログラムを書くことが可能になります。しかし、Ruby などのクラスと全く同じというわけではありません*1


サイ本 pp.170-171 のオブジェクト関係図

サイ本 pp.170-171 の Rectangle と PositionedRectangle の関係は上の図のようになっています。(わざと灰色の矢印で「×」を付けていますが)Rectangle と PositionedRectangle の間には直接の繋がりは無く、プロトタイプチェーンがつながっているということに注意してください。あくまでも継承のシミュレーションです。pp.170-171 の骨子は次の通り。Rectangle.prototype などには名前がなかったので、便宜上 _p という suffix を付けています。*2

たとえば、r.area というプロパティは

  1. r.area
  2. r.__proto__.area
  3. r.__proto__.__proto__.area

という探索で発見されることが分かります。


__proto__ を使わずに、どうやって、赤い矢印をつなげているのか

__proto__ は ECMA 標準ではありませんが、今は、このようなチェーンを作りたいわけです。そこで、new Rectangle として生成したオブジェクト(のプロトタイプオブジェクトは当然 Rectangle.prototype)を prototype にすることで、プロトタイプチェーンを形成しています。本ではこの後に、このオブジェクトから不要なプロパティを delete しています。__proto__ を操作できる方がすっきりしそうなのですが、なんでそうなってないのでしょうか?。


青い矢印の意義

赤い矢印さえあれば、メソッドの探索は可能なのですが、が無いと、PositionedRectangle で作ったオブジェクトは constructor プロパティを持たないため、となってしまうようです。ただ、これが気持ち悪いだけではなく、どのような弊害をもたらすのかは理解できていません。

References
Kanasan.JS JavaScript 第 5 版読書会 #2 [Days on the Moon]
プログラマのためのJavaScript (8):オブジェクト生成の仕組み [檜山正幸のキマイラ飼育記]
__proto__とprototypeについて [guccyonikki]
Kanasan.JS JavaScript 第5版読書会 #2 雑感 [blanket sky]

*1 たとえば、スーパークラスに相当するコンストラクタを取得する方法はありません。constructor プロパティが適切に設定されていて、__proto__ の取得できる環境であれば、a.prototype.__proto__.constructor と辿ることは可能ですが…

*2 ちなみに、Rectangle.prototype.__proto__.__proto__ == Object.prototype のようです。Rectangle.prototype.__proto__ != Function.prototype ですし、いったい、Rectangle.prototype.__proto__ は何なのでしょうか?this.prototype = new Object; ってのが正解なのかなぁ?

_ [JavaScript] Property Accessor とメソッド呼び出し

次のようなプログラムを考えます。 とすると、"say ema"と出力されます。しかし、呼び出し方を変えてみましょう。とすると、どうなるでしょうか?答えは "say " となります。にも関わらず、このようなことがおきます。


これは、Property Accessor 経由で呼び出した場合、this が a になるのに対し、(ブラウザ上だと)sayMethod.say( ) では this が window になるからです。では "say window" と出力されます。変態的挙動ですね。の様に呼び出さなければなりません。


これは、仕様上において Property Accessor が返すのは Function 型のオブジェクトではなく(内部的な表現である) Reference 型のオブジェクトだということに依ります。あくまでも sayMethod 変数に束縛された Function オブジェクトは、自分がどのオブジェクトのプロパティに束縛されていたかは、一切分からないわけです。

Prototype.js では、このような問題を回避するために Function#bind が用意されています。便利ですね。

8.7 Reference 型 (Reference Type)」なども参照下さい。


Ruby の特異メソッドとの違い

JavaScript はオブジェクト毎にメソッドを持つので、Ruby の「特異メソッド」と近い形態だといえます。

しかし一方で、たとえば、Ruby の Object#method で取り出される Method オブジェクトは、bind されているオブジェクトを知っています。さらに、method で取り出された Method オブジェクトは unbind することが出来ない(例外が発生しました)ため、「特異メソッド」では JavaScript のメソッドの振る舞いを模することは出来ません。

JavaScript のプロパティは、その中身に依らず、全てただの入れ物(ポインタのリスト)に過ぎません。

プログラミング言語って、他言語で実装してみようとしないと分からないわ…

_ [JavaScript] JavaScript における mix-in

オブジェクトのプロパティに束縛された関数は、オブジェクトと bind されていないということを利用して、mix-in が実現できます。

方法は、別のオブジェクトのプロパティをコピーするだけです。

たとえば、Prototype.js の Object.extend においてのように、実装されています。

本日のツッコミ(全2件) [ツッコミを入れる]
_ Hiroshi (2008年01月21日 17:18)

JavaScript第5版を 積むだけ勉強法 しただけでは理解できない!!(笑

_ ema (2008年01月22日 13:32)

正直、この辺りはややこしい割に、理解して無くても JS を使うことは出来る内容w。<br>後なんか、第五版はオライリー的な分かりにくさもある。<br><br>しかし、分かってくると単純な仕組みだけど、簡単な解説してるところはないなぁ(この記事含むw)。