読者です 読者をやめる 読者になる 読者になる

tumblr

tumblr(タンブラー)は、メディアミックスブログサービス。ブログとミニブログ、そしてソーシャルブックマークを統合したマイクロブログサービスである。アメリカのDavidville.inc(現: Tumblr, Inc.)により2007年3月1日にサービスが開始された。

prototypeの正体

Javascript

prototypeってものを本で読んだだけでわかったつもりになっていたけど、いざ使ってみたら勘違いばかりでダメダメだったので覚書。やっぱ何か覚えるときはコードの写経だけでもしといたほうがいい気がする。

動かないコード

こんなコードを動かそうとして失敗した。

var proto = {
    prop:"aaaaaa",
    alert:function(){
        alert(this.prop);
    }
};

var base = function(){
this.prototype=proto;
};

var test = new base();
test.alert();


で、こんなエラーが出る。

test.alert is not a function


明らかに

this.prototype=proto;

が怪しいので、こいつをコメントアウトして、さらにbaseコンストラクタの前で

base.prototype=proto;

をしてやる

var proto = {
    prop:"aaaaaa",
    alert:function(){
        alert(this.prop);
    }
};

var base = function(){
//this.prototype=proto;
};

base.prototype=proto;
var test = new base();
test.alert();

こうすると test.alert(); が成功する。
コンストラクタの中でprototypeの指定までできたらスッキリしていいなぁと思ってエラーになってるthis.prototype云々という行をいれたけど、案の定ダメだった。このコンストラクタの宣言の前でprototypeとなるオブジェクト(proto)を定義してあるからよさそうな気もするんだけどなあ。

prototypeと__proto__

var a = function(){this.x = "prop x"};
console.dir(a);

このコードを実行してみる。console.dirで以下のような値が出力された。



「prototype」と「__proto__」というなんか怪しげなプロパティがでてくる。こいつらは何なのか?

var a = function(){this.x = "prop x"};
var b = function(){this.y = "prop y"};
b.prototype = new a();
console.dir(b);
var objB = new b();
console.dir(objB);

今度はこんな感じで2箇所にconsole.dirを設置してみる。
1つ目は関数オブジェクト b に対するもの…(1)
2つ目はbをコンストラクタとして生成されたobjBというオブジェクト…(2)



(1)では「prototype」にaが入り、__proto__は空の関数オブジェクトが格納されている。(2)では「prototype」というプロパティはなく、__proto__にbの「prototype」として追加した a が入っている。どういうこと?

prototypeの役割

そもそもprototypeがしてくれるのは、実行時のプロパティまたはメソッドの検索。これは多くのjavascript入門本なんかにも書かれている。が、ここが紛らわしい(とこだと個人的に思う)。
javascriptは実行時に以下の順序でプロパティを探す。

1. そのプロパティが属するオブジェクト
2. そのオブジェクトの__proto__プロパティとして定義されている__proto__オブジェクト
3. __proto__チェーン上の__proto__オブジェクトを再帰的に検索
4. 最上位のObjectの__proto__オブジェクトにもない場合は undefined を返却

つまりプロトタイプチェーンの実体は__proto__にセットされているオブジェクトを再帰的に検索しているもの。上記の(2)のケースでは objB の__proto__には a が入っていたので、objBのプロトタイプチェーン上には a がいるということになる。なのでalert(objB.x)などとやったら "prop x" という文字列が出力される。


__proto__の役割はわかったけど、じゃあ「prototype」プロパティとはなんなのか?それと__proto__との関係はどうなのか?

コンストラクタによるオブジェクト生成の瞬間

newと関数を組み合わせた形式 new SomeFunc(mayHaveArguments); でオブジェクトが生まれるのですが、そのとき、次の手順に従います。

  1. 処理系が、プレーンなオブジェクトをヒープにアロケートする。
  2. コンストラクタ(として使われる)関数の呼び出し環境(Callオブジェクト、スタックフレームに相当)のthisとして、今アロケートしたオブジェクトをセットする。
  3. コンストラクタ関数のコードが実行される。
  4. thisに入っていたオブジェクトが返される。

大筋はこういうことですね。おっと、大事なことが抜けている。そう、__proto__プロパティのセットです。

●生成したオブジェクトの__proto__プロパティとして、そのコンストラクタ関数のprototypeオブジェクトをセットする。

このことから、オブジェクト生成直後の__proto__オブジェクトは、コンストラクのprototypeオブジェクトと一致することになります。
http://d.hatena.ne.jp/m-hiyama/20050909/1126235062

まぁそういうわけなんです。
「prototype」プロパティはすべての関数オブジェクトがもつプロパティ。なのでそれを基にして上記のような手順でprototypeが__proto__に設定される。
ちなみにこの__proto__というのはjavascriptの独自実装で、ES3には含まれていない。ES5で__proto__と同価の Object.getPrototypeOf() というメソッドが追加された、って最近話題の「パーフェクトjavascript」を立ち読みしたら書いてありました。http://www.ibm.com/developerworks/jp/web/library/wa-ecma262/


参考:http://d.hatena.ne.jp/m-hiyama/20050908/1126154117