Reputation: 57
I have a snippet of javascript code, that I have found in the "Secrets of the JavaScript Ninja" (a John Resig book). I have a problem to understand the behaviour of a variable. The following is the code (simplified with respect to the original one):
(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();
});
};
My problem is the results variable. When you enter the "test" function, the results variable will take the value "ul#results" first and then the value "ul", as a result of the appendChild function. But when you enter the "fn()" function the value of "results" is still "ul#results". Why? I have some difficult to understand the scope of this variable.
Can someone help me to understand this topic?
Thank you very much.
Upvotes: 2
Views: 85
Reputation: 101
This may be more confusing than anything but basically the code is recursively adding some child element to an element in the DOM. This is made possible because of the variable "results" defined in the closure. That variable remains alive within that scope.
BTW, here is an article that shows some tests that explain variable scope in javascript. Keep in mind that it does NOT talk about closures, like you have here. But it may help explain some other things you'll run into.
http://www.computerhowtoguy.com/an-introduction-to-javascript-variable-scope/
This is the process explained step by step:
The DOM structure would at this point look something like this:
<div id="results">
<li>
<ul>
<li></li>
</ul>
</li>
</div>
And so the code moves on with the same sort of thing...
This is the code revised, now with comments:
// 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();
}
);
};
Upvotes: 0
Reputation: 318488
The variable is created in the scope of the anonymous function. Both assert
and test
access the same results
variable.
Upvotes: 2