2

「 Secrets of the JavaScript Ninja」(John Resig の本)で見つけた JavaScript コードのスニペットがあります。変数の動作を理解するのに問題があります。以下はコードです (元のコードより簡略化されています)。

(function() {
    var results;
    this.assert = function assert() {
        var li = document.createElement("li");
        results.appendChild(li);
        return li;
    };
    this.test = function test(name, fn) {
        results = document.getElementById("results");
        results = assert().appendChild(document.createElement("ul"));
        fn();
    };
})();
window.onload = function() {
    test("A test.", function() {
        assert();
        assert();
    });
};

私の問題は結果変数です。"test" 関数を入力すると、結果変数は、 appendChild関数の結果として、最初に値"ul#results"を取得し、次に値"ul"を取得します。しかし、「fn()」関数に入ると、「results」の値は「ul#results」のままです。なんで?この変数の範囲を理解するのが難しいことがあります。

誰かがこのトピックを理解するのを手伝ってくれますか?

どうもありがとうございました。

4

2 に答える 2

2

変数は無名関数のスコープで作成されます。両方とも同じ変数asserttestアクセスします。 results

于 2012-07-09T20:07:42.740 に答える
0

これは何よりもややこしいかもしれませんが、基本的にコードは DOM 内の要素に子要素を再帰的に追加しています。これは、クロージャで定義された変数「results」により可能になります。その変数はそのスコープ内で存続します。

ところで、これは JavaScript の変数スコープを説明するいくつかのテストを示す記事です。ここにあるように、閉鎖については話していないことに注意してください。しかし、それはあなたが遭遇する他のいくつかのことを説明するのに役立つかもしれません.

http://www.computerhowtoguy.com/an-introduction-to-javascript-variable-scope/

これは、段階的に説明されたプロセスです。

  1. window.onload 関数のコードには、「ウィンドウ」レベルのスコープがあります。「this」を出力すると、「window」オブジェクトが得られます。
  2. onload 関数は「test」を呼び出します。this.test または window.test とも書ける「test」は、クロージャー内にあります。クロージャの "test" 関数である理由は、次の行のためです: this.test = ... クロージャはウィンドウ レベルで実行されたため、"this" は "window" オブジェクトを参照します。
  3. "test" 関数呼び出しでは、"this" は "window" オブジェクトを参照します。上記のアサート関数は this.assert = ... (window.assert) で定義されているため、ウィンドウ オブジェクト レベルにあります。assert == window.assert を実行すると、"true" が返されます。これは、どちらも同じ (関数) オブジェクトの 2 つの名前にすぎないためです。
  4. 次の行が実行されます: assert().appendChild(document.createElement("ul")) assert 関数は、それ以降の関数より先に実行されます。それでは、アサートコードに入りましょう...
  5. assert では、「this」は依然として「window」オブジェクトです。ドキュメント要素にまだ関連付けられていない新しい DOM 要素を参照するローカル変数 "li" が作成されます。次に、この新しい li 要素を "results" DOM 要素に追加します。現在はドキュメント DOM オブジェクトの一部です。li 要素への JavaScript 参照が返されます。
  6. assert().appendChild(document.createElement("ul")) appendChild 関数が呼び出され、新しく作成された 'li' 要素に新しい 'ul' 要素が追加されます。次に、JavaScript の "results" オブジェクトが再度割り当てられます。今回は、新しく作成された ul 要素に割り当てられます。最後に、'fn' (以前の無名関数) が呼び出されます。
  7. 「fn」...「this」はまだウィンドウを指します。assert が呼び出され、assert は依然として window.assert 関数への参照です。新しい li 要素が作成され、"results" 変数に追加されます。"results" が最後に割り当てられたのは ul 要素だったので、新しい li を追加していることを思い出してください。

この時点で、DOM 構造は次のようになります。

<div id="results">
    <li>
        <ul>
            <li></li>
        </ul>
    </li>
</div>

そして、コードは同じ種類のもので進みます...

これは改訂されたコードで、コメントが追加されています。

// the following is a closure.  a sort of isolated container with its own scope.
(function() {
    // results is not globally scoped, only scoped at the closure level,
    // since its defined with "var".
    var results;

    // "this" is the calling object. (ie: window object)
    this.assert = function assert() {

        // since "var" is used "li" is part of this function.
        var li = document.createElement("li");

        // results (at the closure level) appends a child at this function's level.
        results.appendChild(li);

        // return a javascript reference to the new DOM element.
        return li;
    };

    // again "this" is the calling object.  when called in onload below, "this" is the window object.
    this.test = function test(name, fn) {

        // results refers to the closure level results variable, since var is ommitted.
        // this is a reference to an element in the DOM.
        results = document.getElementById("results");

        // changing the variable now. the DOM object "results" is NOT altered by this assignment, since
        // javascript is separate from the DOM.
        // NOTE: the assert function was previously assigned to the window object previously.  so stuff in that
        // function will be window scoped.
        results = assert().appendChild(document.createElement("ul"));

        // call fn
        fn();
    };
})();

window.onload = function() {

    // at this point, "this" is the "window" object.
    // "test" is part of the closure above.  in the closure the test function is assigned
    // to "this".  since we are calling the function here, "this" will be the window object in the
    // closure for this call.
    test("A test.", 
        // an anonymous function.  this is really just an object passed into the "test" function
        function() {
            assert();
            assert();
        }
    );
};
于 2012-10-24T21:23:27.717 に答える