ノリでなんとなく書いていても駄目かなあと強く思うことが増えてきたので、今さらながらjsの基本をしっかり覚えていこうと思うようになってきました。
とくにプロパティまわり。
今までprototype以外のプロパティをあんまり意識した事がなかったのですが、最近それじゃ理解が深まらないなあと。
まずはオブジェクトという概念について、色々とサイトをめぐりつつ学習……
- javascriptでは、全てがオブジェクト
- 数値もNumberというオブジェクト
- functionキーワードは、Functionオブジェクトを作るシンタックスシュガー
- constructorプロパティは、対象オブジェクトを実体化するときに実行される関数
- 関数オブジェクトでは、このconstructorをthisというスコープを抱えつつ実体化することで自由変数への参照を持つ関数(すなわちクロージャー)にすることが出来るので、みんなこれをクラスのように扱う。
- また関数オブジェクトは定義時にprototypeというプロパティが設定され、これが同一関数オブジェクト間で共通して参照できるスコープとなる。
- これによって、関数オブジェクトでは独自にインスタンス化できる部分と、共通に利用できる部分のメモリを分ける事が出来る。
こういう流れで、constructorというプロパティが気になったので、次の実験をしてみることに。
js> var hoge = function(){ print(constructor); }; js> new hoge(); function Object() { [native code for Object.Object, arity=1] } [object Object]
どうやら、constructorプロパティには、hoge関数オブジェクト「そのもの」を作る関数オブジェクトが入っている模様。
で、関数オブジェクトそのものはファーストクラスのオブジェクトだから、それを作る実装にはネイティブコードが入っている、と。
じゃあ、new hoge() とかして、hoge関数オブジェクトをインスタンス化するときに実行される、function(){ print(constructor); } というコードはどこに? と思い、なんとなく次のように書き換えて、new hoge() してみることに。
js> var hoge = function(){ print(this.constructor); }; js> new hoge(); function () { print(this.constructor); } [object Object]
おお、ちゃんとインスタンス化のコードが出てきました。
でも、関数定義の内部で特別に定義したわけでもないのに、this.constructorとはなんなんだ、と不思議に思ってしまいました。
なので、まずは次の実験。
js> hoge.constructor; function Function() { [native code for Function.Function, arity=1] }
これは先ほどと同じく関数オブジェクトを作るネイティブ実装が返ってきました。むむむ。
そこでしばし考え……
js> hoge.prototype.constructor; function () { print(this.constructor); }
とか打ってみると、ビンゴ!
先ほどのnew hoge() した時に出力させたthis.constructorと同じ値が返ってきました。
ということは……なるほど。
つまり、このconstructorとthis.constructor、どっちもプロパティなんですね。
前者はhoge関数オブジェクトのローカル(?)プロパティ。
で、後者のthis.constructorは、ローカルにないからプロトタイプチェーンをめぐり、プロトタイプに定義されているconstructorプロパティにヒットしたもの、ということでしょうかね。
よく考えてみれば、関数オブジェクトを複数実体化するとき、その実体化するためのコードそのものは当然ながら全てのインスタンスで共通に利用するものなので、prototypeにそのコードがセットされているのは自然なこと……なのでしょうか。