11 Mar 2011

Window オブジェクトは厳密には Global オブジェクトじゃない?

サイ本6版の 14.8 章に "The WindowProxy Object" というコラムがありました. 曰く, クライアントサイド JavaScript における Window オブジェクトは Global オブジェクトだと言われるが, 厳密な意味ではこれは誤りである, と. これは知らない話だったので, 理解のためにまとめます.

注: 普通に js を書く上では特に必要のない知識です.

まずは前提のおさらい

まず前提として, 以下の点を確認します.

  • クライアントサイド JavaScript のグローバルオブジェクトは, Window オブジェクトである (と本などではよく述べられている)
  • iframe や window.open() などで複数の window がある場合, それぞれの Window オブジェクトは独立したコンテキストである.
  • Window オブジェクトはロードのたびにリフレッシュする.

問題

では, 次のようなケースを考えます. window.open() し, その返り値を保持します. 返り値は open() された子 window の Window オブジェクトへの参照です.

var w = window.open(); // w は open したウィンドウの window オブジェクト

ここで, 子ウインドウをリロードしたとしましょう. 子ウインドウの window オブジェクトは破棄され, 新しく作り直されます. そうすると変数 w の指す先がなくなり, 子ウインドウを参照することができなくなってしまいます.

これでは不便です. そして, 現実にもこの様な挙動にはなっていません. では, どの様にこの問題を解決しているのでしょうか.

WindowProxy Object

解決法は素直です. proxy を導入します.

グローバルオブジェクトはスコープチェーンのトップレベルにあるオブジェクトで, ロードのたびにリフレッシュします. Window オブジェクトはグローバルオブジェクトと同一ではなく, 現在の (current) グローバルオブジェクトを指すポインタのような存在とします.

Window オブジェクトは常にカレントグローバルオブジェクトへの参照を保持しています. Window オブジェクトのメソッドを実行したり, プロパティにアクセスしたりした場合, グローバルオブジェクトのメソッドが呼び出されたり, プロパティがアクセスされます.

こうすることで, 先ほどの問題が解決できます.

HTML5 の仕様書では, この様な proxy オブジェクトを明示的に WindowProxy と読んでいるそうです.

実際

実際のところ, proxy オブジェクトとグローバルオブジェクトを見分けることは不可能です. window, self, top, parent, frames プロパティは WindowProxy オブジェクトを返しますし, トップレベルに定義されている関数の this にもグローバルオブジェクトではなく WindowProxy が入っています.

HTML5 仕様上での WindowProxy オブジェクト

ここまではサイ本を読んだ上での情報ですが, HTML5 の仕様も確認してみます.

仕様では "5.1 Browsing contexts" や "5.2.7 The WindowProxy object" で WindowProxy オブジェクトについて説明されています.

As mentioned earlier, each browsing context has a WindowProxy object. This object is unusual in that all operations that would be performed on it must be performed on the Window object of the browsing context's active document instead. It is thus indistinguishable from that Window object in every way until the browsing context is navigated.

この文を読むと, Window Object === Global Object で, WindowProxy がアクティブな Window を指しているととれます. 表現は微妙にサイ本と違うのですが, 内容は同じです.

まとめ

WindowProxy オブジェクトは常にカレントなグローバルオブジェクトを指す proxy オブジェクトですという話でした. 普通に js で開発を行う分には必要のない知識です. HTML5 の仕様書を読む時には役に立つのかもしれません.