这可能比任何事情都更令人困惑,但基本上代码是递归地将一些子元素添加到 DOM 中的元素。这之所以成为可能,是因为闭包中定义了变量“结果”。该变量在该范围内仍然有效。
顺便说一句,这是一篇文章,展示了一些解释 javascript 中变量范围的测试。请记住,它不像您在这里所说的那样谈论闭包。但这可能有助于解释您将遇到的其他一些事情。
http://www.computerhowtoguy.com/an-introduction-to-javascript-variable-scope/
这是逐步解释的过程:
- window.onload 函数中的代码将具有“窗口”级别的范围。如果你要输出“this”,你会得到“window”对象。
- onload 函数调用“测试”。“test”,也可以写成 this.test 或 window.test,在一个闭包中。它是闭包中的“test”函数的原因是因为这行: this.test = ... 由于闭包是在窗口级别执行的,“this”指的是“window”对象。
- 在“test”函数调用中,“this”指的是“window”对象。由于上面的断言函数定义为: this.assert = ... (window.assert) 它位于窗口对象级别。如果你确实 assert == window.assert 你会得到“真”,因为两者只是同一个(函数)对象的两个名称。
- 运行这一行: assert().appendChild(document.createElement("ul")) 断言函数在它之后的任何内容之前执行。所以让我们进入断言代码......
- 在断言中“this”仍然是“window”对象。创建了一个局部变量“li”,它引用了一个尚未附加到文档元素的新 DOM 元素。然后我们将这个新的 li 元素附加到“results”DOM 元素中。现在它是文档 DOM 对象的一部分。返回对 li 元素的 javascript 引用。
- 现在回到: assert().appendChild(document.createElement("ul")) 调用 appendChild 函数,将新的 'ul' 元素附加到新创建的 'li' 元素。接下来,我们的 javascript“结果”对象再次被重新分配,这次是新创建的 ul 元素。最后,'fn'(我们从前的匿名函数)被调用。
- 进入“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();
}
);
};