2

我有一段 javascript 代码,我在“ JavaScript Ninja 的秘密”(John Resig 的书)中找到了它。我很难理解变量的行为。以下是代码(相对于原始代码进行了简化):

(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”函数时,结果变量将首先取值“ul#results”,然后取值“ul”,这是appendChild函数的结果。但是当你输入“fn()”函数时,“results”的值仍然是“ul#results”。为什么?我有些难以理解这个变量的作用域。

有人可以帮我理解这个话题吗?

非常感谢。

4

2 回答 2

2

该变量是在匿名函数的范围内创建的。两者都assert访问test一个 results变量。

于 2012-07-09T20:07:42.740 回答
0

这可能比任何事情都更令人困惑,但基本上代码是递归地将一些子元素添加到 DOM 中的元素。这之所以成为可能,是因为闭包中定义了变量“结果”。该变量在该范围内仍然有效。

顺便说一句,这是一篇文章,展示了一些解释 javascript 中变量范围的测试。请记住,它不像您在这里所说的那样谈论闭包。但这可能有助于解释您将遇到的其他一些事情。

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

这是逐步解释的过程:

  1. window.onload 函数中的代码将具有“窗口”级别的范围。如果你要输出“this”,你会得到“window”对象。
  2. onload 函数调用“测试”。“test”,也可以写成 this.test 或 window.test,在一个闭包中。它是闭包中的“test”函数的原因是因为这行: this.test = ... 由于闭包是在窗口级别执行的,“this”指的是“window”对象。
  3. 在“test”函数调用中,“this”指的是“window”对象。由于上面的断言函数定义为: this.assert = ... (window.assert) 它位于窗口对象级别。如果你确实 assert == window.assert 你会得到“真”,因为两者只是同一个(函数)对象的两个名称。
  4. 运行这一行: assert().appendChild(document.createElement("ul")) 断言函数在它之后的任何内容之前执行。所以让我们进入断言代码......
  5. 在断言中“this”仍然是“window”对象。创建了一个局部变量“li”,它引用了一个尚未附加到文档元素的新 DOM 元素。然后我们将这个新的 li 元素附加到“results”DOM 元素中。现在它是文档 DOM 对象的一部分。返回对 li 元素的 javascript 引用。
  6. 现在回到: assert().appendChild(document.createElement("ul")) 调用 appendChild 函数,将新的 'ul' 元素附加到新创建的 'li' 元素。接下来,我们的 javascript“结果”对象再次被重新分配,这次是新创建的 ul 元素。最后,'fn'(我们从前的匿名函数)被调用。
  7. 进入“fn”...“this”仍指window。assert 被调用,assert 仍然是对 window.assert 函数的引用 创建一个新的 li 元素并将其附加到“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 回答