プログラムな日常>日常>
javascript における視野、続編
(scope in javascript programs --- sequel)

 javascript における視野 で不十分なことが多々あったので、続きを書きます。

【複数の関数定義】
 C# などでは、同じ名前の関数は定義できないが、javascript ではこれができてしまう。
 地の文(他のfunction の内側でない)においては、function foo() { }, foo=function() { }, this.foo=function() { }, window.foo=function() { } などは (選ばれた場合には)全て同じ効果になり、最後に定義されたものが選ばれ、実行されるが、function foo() { } だけは独特で、これだけが最初に定義される。

さらに、この function foo() { } に限っても、同じ名前のものを複数置くことが可能で、その中で、文脈上最後のものが採用される。
 このことから、解釈は2パスで実行され、1パス目は、var の見出しと、function foo() { } の「登録」が、また2パス目は変数の計算などと共に foo=function() { } などの「登録」がなされると考えられる。 ただし、ここに言う「登録」とは中身の実行のことではなく、多分、関数名の見出しに結び付けて、個々の関数の内容か範囲が登録されるのだろう。

 2パス目はともかく、1パス目の関数が複数設置できる理由、用途はやや不明だが、長いプログラムの場合、最近の関数を信用せよということなのかも知れない。 そうは言っても、this.foo=function() { } の呼び出しが随分前にあると、これに打ち消される危険はある。また、かなり後の方にある function foo() { } のような関数にも注意しなければならない。 このことから、地の文に何でも書くのは、見つけにくいバグの原因になる危険があるかも。
 ただし、このどちらの現象も、ファイルを分けた場合には起こらないようだ。 ということは、1パス2パスはファイル別に連続して行われるのかも。 手近、と言うか、同じファイル内に呼び出される関数がない場合、先のファイルで定義される関数、下の場合で言うと this.A=function() は利用されるが、後から定義される function B() が同ファイル上にはない場合、視野外のため、エラーになる。

 次に関数内の中のことだが、(new で呼び出していない)関数の中では、this は window である。 しかしこれは、this が window だというだけのことで、何もついていない変数や、 function foo() { } 、また、foo=function() { } といった関数も this や window がついたものと同じというわけではない。 それらはあくまでローカルな変数、関数だ。new で呼び出した場合にはまた違う話になるので、一度整理しておく方が良い。
   地の文:           window = this = [Local]
   new しない function の中:  window = this ≠ [Local]
   new した function の中:   window ≠ this(= object) ≠ [Local]
 ここで「new しない function」と言うのは、new した function の中に入れ子になって最内側で new しないものを含む。 また、このローカル変数だが、該当のものがなければ、外側のものが順次検索されるのは先の頁で static binding として説明した通りだ。これは次の例により明らかだ。

 new した場合にも同じであることを確かめておこう。

 new する、しないに拘わらず、関数の内側では、function foo() { } と foo=function() { } とは実行される限りは同じもの、ただし、「登録」の順序が変わる。 new しない場合において、this.foo=function() { } はグローバルな関数となるので、先の2つとは別物、window.*** は this.*** と同じだ。これは次の例に示される。

 先ほどの整理をもう一度見て頂こう。
   地の文:           window = this = [Local]
   new しない function の中:  window = this ≠ [Local]
   new した function の中:   window ≠ this(= object) ≠ [Local]
 new した場合には、object が作られ、これが this になるので、window との不一致が生じる。これはまあ良いとして、object に属する変数、関数は、ローカルなものとはまた別のものとなる。 これがかなりわかりにくく、実務の上でも混乱し易い点だ。new した関数の中では、window、this(=object)、ローカル と、都合3種類の同名の関数が並立する可能性がある。

 new した関数はオブジェクトとして保存でき、その中の変数は保存されている。これはローカルな変数も同様(前編からの修正です)であり、内部の関数を通じて値を参照できる。

 オブジェクトとして保存される関数は入れ子にすることができる。次のように。