檜山正幸さんの記事にインスパイアされて、というか、内容を理解するために、メモ。

データ、オブジェクト構造、コンパイルと実行、宣言とスコープ、関数、オブジェクト生成、プロトタイプチェーン

まずは、データについて。データは6種類に分かれる。
number型、boolean型、string型、object型、function型、undefined型の6つ。
リテラル、変数、式、をtypeof演算すると上の6つの文字列のどれかが返される。
number、boolean、string、null, undefinedはイミュータブル(変更不可能)である。つまり、演算を作用させてもデータは変更されない。
イミュータブルなデータは単一のメモリブロックによって表現される。位置情報は無関係。等価性は、そのメモリブロックのビット列の等価性で決定する。

対して、objectはミュータブル(変更可能)である。つまり演算の作用として、データを変更できる(というか変化することが期待されるデータである)。オブジェクトは「性質」と「振る舞い」を持つことが期待される、つまり、データには値と、位置情報があり、プログラムはデータ位置情報から値を参照する。
※stringは実装上はおそらく参照を使っている。けれど、言語仕様としてはprimitiveとして扱っている。

number, boolean, stringは以上のとおり、primitiveである。関数Number, Boolean, Stringそれぞれが、primitive->object型変換を行う(型変換関数によって返されるオブジェクトはいわば「ラッパーオブジェクト」である。)

次にオブジェクト構造。
オブジェクトの情報と機能は、必ずオブジェクトの名前を通じてアクセスされる。この名前を「プロパティ」と言う。
そのイメージは、グラフである。
ノードがデータそのものであり(プリミティヴなデータも含まれる)、ノードから他のノードへの辺が参照である。辺には「必ず」名前が付けられ、それがプロパティ。(プロパティがノードに直接くっついていないことに注意)。オブジェクトたちは、このグラフ構造(ツリー構造ではない!従って循環もあり得る)の中に存在している。
javascriptには無名のグローバルオブジェクトが必ず見えている。これが、グラフ構造におけるルートとなる。ブラウザであれば、windowという名前がそれをさす。あるいは、トップレベルではthis演算子がグローバルオブジェクトを指す。

次に、コンパイルと実行。
javascriptソースコードは、「1:コンパイラによって処理系(javaでいうVMみたいなもの)に依存したコード列にコンパイルされ」、「2:そのコード列がそのVMによって実行される」。コンパイル中も、実行中もオブジェクト構造(上で述べたグラフ構造)は変化する(だからこそオブジェクトには位置情報があるわけで)。また、コンパイル後、直ちに実行されるので、ユーザはコンパイルを意識する必要がない(つまりコンパイルされたコード列(=トップレベルのコード列)は実行後は不要となる)。コンパイル単位は、ブラウザであればファイルである。つまりファイルに格納されたコード全てが一度にコンパイルされる(1行ごと、ではない)。
全体を眺める(これを「系」と呼ぶのが用語として正しいか・・・)と、javascriptの処理系は、コンパイラVM・オブジェクト構造からなる、ととらえられる。この系の中で、全てが行われる。プログラム走行中に起こることは全てオブジェクト構造に記録される。実装はブラウザやその他etcさまざまであるが、処理系はこのオブジェクト構造を全て把握している(つまりプロパティを知っている)。ユーザー(=プログラマ)に開放されていないプロパティもあり、それは処理系によって利用される。

次に宣言とスコープ。
function文、とvar文は、宣言である。宣言とは、コンパイラに出す指示、と考えてみる。すなわち、function文,var文は、コンパイルフェーズで処理される。ソースコード上の全ての関数定義(関数式でない!!)は、コンパイルフェーズで実行可能なオブジェクトとしてヒープに存在しているので、いつでも実行できる。var文は、コンパイル時に変数をメモリブロック内に確保し、undefined値で初期化される。undefinedは0, null, NaNとも違う値で、”未定義”を意味する値である。グローバルオブジェクトのプロパティとしてundefinedプロパティがあり、これはundefinedという値をさしている。
javascriptにはグローバルスコープとローカルスコープの二つしかない。グローバルスコープとは、グローバルオブジェクトそのものであり、ローカルスコープとは呼び出しいてる関数オブジェクトそのものである。そう考えると、「変数」というのは、スコープのプロパティと考えてよい(クロージャイベントハンドラは別)。
「したがって」ブロックスコープは存在しない。


次に関数。
関数もこの中にあっては、オブジェクトであるので、プロパティ(&内部プロパティ)を持つ。関数の持つデータはコード(=上で述べたコンパイル済コード列=ビット列)である(これはユーザーには開放されていない)。
関数を呼び出す、あるいは実行する、というのは、適当なセットアップの後、関数オブジェクトの、上で述べた内部プロパティの指すコード列を処理系の中で実行することである。このセットアップとしてコンパイルがある。
トップレベルの関数のソースコード一つ一つから、コンパイラが処理系に依存したビット列を生成し、さらにそれに対応した関数オブジェクトと、グローバルオブジェクトも生成する。こうして、関数は呼び出すことのできる(=実行できる)オブジェクトとなる。

次にオブジェクトの生成。
オブジェクトはnew演算子と関数オブジェクトで行う。ここでは、new演算子の作用する関数オブジェクトのことをコンストラクタという、という風に理解してみる。なお、リテラル表記もあるけれど、これは処理系が生成と初期化を同時に行ってくれている。***生成・と初期化は別である!!***
new演算子は次のことを行う。
まず、処理系が中身のないオブジェクトを生成し、ヒープにアロケートする。次に、コンストラクタのthisにこのオブジェクトをセットする、次に、コンストラクタを実行、最後に、このオブジェクトの内部プロパティ__proto__にコンストラクタのprototypeプロパティをセットしてオブジェクトが返される。
関数オブジェクトには、prototypeプロパティが必ずあり、prototypeプロパティなにかしらの原型オブジェクトを指している。オブジェクトの生成、つまりnew演算子は、その原型へのチェインを形成するために、__proto__にその情報を渡している。__proto__にはconstructorプロパティあり、これがnew演算子の作用子であるコンストラクタオブジェクトを指している(=親を認識できる、自分と親を区別できるということ)。

コンストラクタオブジェクトにはprototypeプロパティがあり、それは上でも述べた原型となるオブジェクト(これをプロトタイプオブジェクトと呼んでみる)を指している。
そしてプロトタイプオブジェクトオブジェクトには、constructorプロパティがあり、それは、コンストラクタオブジェクトを指している。
つまり、prototypeプロパティとconstructorプロパティはそれぞれ対を成していて、それぞれプロトタイプオブジェクト・コンストラクタオブジェクトを指している。


次にプロトタイプチェーンについて。
javascriptの全てのオブジェクトには__proto__という内部プロパティがある。__proto__には以下の性質がある:
1:__proto__の値は、nullもしくは自分とは違うオブジェクトである。
2:x.__proto__, x.__proto__.__proto__, x.__proto__.__proto__.__proto__, ・・・という列(これをプロトタイプチェーンという)はxには戻らない。
3:2のチェーンは、nullが出現した時点で終わる。

このチェーンは、プロパティの検索、つまり"."演算子の処理に用いられる。"."演算子は、作用するオブジェクトのプロパティを検索するが、自身にプロパティを見つけられない場合は、このチェーンをたどって、プロパティを検索し、そのプロパティの指すオブジェクトを返す。無い場合は、undefinedを返す。

次に一応メソッド、について。
メソッドとは、関数オブジェクトを値に持つプロパティである。なので、メソッドの検索も、プロトタイプチェーンを使って行われる。