グローバルオブジェクトの参照はも function 宣言時に決まる
以前「window オブジェクトを汚さないネタ」で疑問に思ったので検証しました。
parent.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html><head><title>Window テスト(Parent)</title> <script type="text/javascript"> window.name='parent'; function hello(){ alert('hello:'+window.name); } function openChild(){ var w=open('child.html','','width=400,height=400'); w.parentHello=hello; } </script> </head> <body> <p><button onclick="openChild()">openChild</button></p> </body> </html>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html><head><title>Window テスト(Child)</title> <script type="text/javascript"> window.name='child'; function hello(){ alert('hello:'+window.name); } window.parentHello2=parentHello; window.parentHello3=function(){ parentHello(); }; window.parentHello4=function(){ parentHello.call(window); }; window.parentHello5=function(){ eval('('+parentHello.toString()+')()'); }; </script> </head> <body> <p><button onclick="hello()">childHello</button> 「hello:child」</p> <p><button onclick="parentHello()">parentHello</button> 「hello:parent」</p> <p><button onclick="parentHello2()">parentHello2</button> 「hello:parent」</p> <p><button onclick="parentHello3()">parentHello3</button> 「hello:parent」</p> <p><button onclick="parentHello4()">parentHello4</button> 「hello:parent」</p> <p><button onclick="parentHello5()">parentHello5</button> 「hello:child」</p> </body> </html>
結果は表題の通り「グローバルオブジェクトの参照は function 宣言時に決まる」ようです。Opera、Firefox、IE で同じ結果になったのでほっとしました。
で、これはやはりクロージャが作用しているものだと思われます。
関数内に出現するフリー変数(関数内で宣言されていない変数)の解決の際、実行時の環境ではなく、関数を定義した環境の変数を参照する機能。
僕はこれを「関数を定義した環境のローカル変数」と解釈していたのですが、グローバルオブジェクトも例外ではないようです。
これを「特定の window オブジェクトに依存しない書き方」にするには、例えば以下のようにします。
parent.html
(略) <script type="text/javascript"> (function(){ var win=window; window.LIB = { setEnvironment: function(newWin){ win=newWin; }, flexibleHello : function(){ win.alert('hello:'+win.name); } } })() (略) function openChild(){ var w=open('child.html','','width=400,height=400'); w.parentHello=hello; w.LIB=window.LIB; } </script> (略)
(略) <p><button onclick="LIB.flexibleHello()">flexibleHello</button> 「hello:parent」setEnvironment後は「hello:child」</p> <p><button onclick="LIB.setEnvironment(window);LIB.flexibleHello()">setEnvironment→flexibleHello</button> 「hello:child」</p> (略)
実行前にグローバルオブジェクトをライブラリに渡して、ライブラリ内ではグローバルオブジェクトに一切アクセスしないというやり方です。
こうしておけば複数の window 間でライブラリを共有できるのですが、なかなかそのようなライブラリは見かけませんね。まあそれぞれの window でライブラリをロードすればいいだけなので問題は無いのですけれども。