62

コードを貼り付ける前に、シナリオを次に示します。

  1. JavaScript を使用して空の iframe を作成する HTML ドキュメントがあります。
  2. JavaScript は関数を作成し、その関数への参照を iframe のドキュメント オブジェクトにアタッチします (ドキュメントdoc.open()への参照を取得するために使用します)。
  3. 関数はonload、iframe のドキュメントのハンドラーとしてアタッチされます ( <body onload="...">iframe.

ここで困惑したのは、onload ハンドラー内のグローバル (ウィンドウ) オブジェクトとドキュメント オブジェクト (実行中) が、スクリプト ノードを介して追加された JavaScript を介して実行される同じオブジェクトとは異なることです。

HTMLは次のとおりです。

<!doctype html>
<html>
<head>
<script>
(function(){
  var dom,doc,where,iframe;

  iframe = document.createElement('iframe');
  iframe.src="javascript:false";

  where = document.getElementsByTagName('script')[0];
  where.parentNode.insertBefore(iframe, where);

  doc = iframe.contentWindow.document;

  var _doc = document;

  doc.open()._l=function() {
    // the window object should be the one that doc is inside
    window.vanishing_global=new Date().getTime();

    var js = this.createElement("script");
    js.src = 'test-vanishing-global.js?' + window.vanishing_global;

    window.name="foobar";
    this.foobar="foobar:" + Math.random();
    document.foobar="barfoo:" + Math.random();

    // `this` should be the document object, but it's not
    console.log("this == document: %s", this == document);
    console.log("this == doc:      %s", this == doc);

    // the next two lines added based on @Ian's comment below
    console.log("_doc == document: %s", _doc == document);
    console.log("_doc == doc:      %s", _doc == doc);

    console.log("name: " + window.name + "\n" + "window.vanishing_global: " + window.vanishing_global + "\ntypeof window.vanishing_global: " + typeof window.vanishing_global + "\ndocument.foobar: " + document.foobar);
    this.body.appendChild(js);
  };
  doc.write('<body onload="document._l();"></body>');
  doc.close();
})();
</script>
</head>
<body>
</body>
</html>

そして、ここにありますtest-vanishing-global.js

console.log("name: " + window.name + "\n" + "window.vanishing_global: " + window.vanishing_global + "\ntypeof window.vanishing_global: " + typeof window.vanishing_global + "\ndocument.foobar: " + document.foobar);

指示:

これら 2 つのファイルをディレクトリに配置し、ブラウザーで HTML を開きます (最新の Chrome と Firefox でテストされ、両方で同じ結果が得られました)。

これは私が得る出力です:

this == document: false
this == doc:      true
_doc == document: true
_doc == doc:      false

name: foobar
window.vanishing_global: 1366037771608
typeof window.vanishing_global: number
document.foobar: barfoo:0.9013048021588475

name: 
window.vanishing_global: undefined
typeof window.vanishing_global: undefined
document.foobar: foobar:0.5015988759696484

ハンドラー内のthisオブジェクトは、ドキュメント オブジェクトのいずれかである必要があります。これドキュメント オブジェクトですが、内部で実行されるドキュメントと同じドキュメント オブジェクトではありません (親ドキュメントとも同じではありません)。ハンドラー内のウィンドウ オブジェクトも、ページに読み込まれた JavaScript で実行されるウィンドウ オブジェクトと同じではありません。

最後に私の質問:

何が起こっているのか、実際のウィンドウオブジェクトへの参照を取得する方法、または少なくとも同じグローバルコンテキストからグローバル変数を宣言して参照する方法を知っている人はいますか?

脚注:

この iframe は同じドメイン上にあるため、クロスドメインの問題はありません。誰かが を設定すると問題が発生しますdocument.domainが、このサンプル コードではそれが行われていません。

4

1 に答える 1

69

親ページですべてを宣言しています。したがって、windowおよびdocumentへの参照は親ページのものです。に何かをしたい場合はiframe、 を使用iframe || iframe.contentWindowして にアクセスしwindowiframe.contentDocument || iframe.contentWindow.documentにアクセスしますdocument

何が起こっているのか、おそらく「レキシカル スコープ」という言葉があります:レキシカル スコープとは何ですか?

スコープの唯一のコンテキストはこれです。そしてあなたの例では、メソッドの所有者は でありdoc、これはiframeですdocument。それ以外は、既知のオブジェクトを使用するこの関数でアクセスされるものはすべて親のものです (関数で宣言されていない場合)。関数が別の場所で宣言されている場合は別の話ですが、親ページで宣言されています。

これは私がそれを書く方法です:

(function () {
  var dom, win, doc, where, iframe;

  iframe = document.createElement('iframe');
  iframe.src = "javascript:false";

  where = document.getElementsByTagName('script')[0];
  where.parentNode.insertBefore(iframe, where);

  win = iframe.contentWindow || iframe;
  doc = iframe.contentDocument || iframe.contentWindow.document;

  doc.open();
  doc._l = (function (w, d) {
    return function () {
      w.vanishing_global = new Date().getTime();

      var js = d.createElement("script");
      js.src = 'test-vanishing-global.js?' + w.vanishing_global;

      w.name = "foobar";
      d.foobar = "foobar:" + Math.random();
      d.foobar = "barfoo:" + Math.random();
      d.body.appendChild(js);
    };
  })(win, doc);
  doc.write('<body onload="document._l();"></body>');
  doc.close();
})();

windocasのエイリアシングは必要wありdません。スコープの誤解による混乱を軽減するだけかもしれません。このように、それらはパラメーターであり、iframeのものにアクセスするにはそれらを参照する必要があります。親にアクセスしたい場合は、引き続き and を使用windowdocumentます。

documentdocこの場合)にメソッドを追加することの意味はわかりませんが、_lメソッドを onに設定する方が理にかなっているかもしれませんwin。そうすれば、プレフィックスなしで実行できます...など<body onload="_l();"></body>

于 2013-04-15T15:56:18.500 に答える