So, I'm still reading Apress Pro Javascript Techniques and i'm having troubles with closures.
As John Resig states:
Closures allow you to reference variables that exist within the parent function. However it does not provide the value of the variable at the time it is created; It provides the last value of the variable withing the parent function. The most common issue under which you'll see this occurr during a for loop. There is one variable being used as an interaor (e.g., i). Inside of the for loop, new functions are being created that utilize the closure to reference the iterator again. The rpoblem is tat the time the new closured functions are called, they will reference the last value of the iterator (i.e., the last position in an array), not the value taht you woul expect.
Then he presents, in listing 2-16 an example using anonymous functions to induce scope.
/**
* Listing 2-16. Example of Using Anonymous Functions to induce the
* Scope Needed to Create Multiple Closure-Using Functions
*/
// An element with an ID of main
var obj = document.getElementById("main");
// An array of items to bind to
var items = ["click", "keypress"];
for (var i = 0; i < items.length; i++) {
// Use a self executed anonymous function to induce scope
(function() {
// Remembre the value within this scope
var item = items[i];
// Bind a function to the element
obj["on" + item] = function() {
// item refers to a parent variable that has been successfully
// scoped within the context of this loop
alert("thanks for your " + item);
};
})();
}
This example works as expected, and the behavious of the main object is correct.
The in the following, it uses another time a self-executing function to induce scope, during an iteration.
The purpose of the function is to create an object, defining getters and setters for all its properties. In this case, the example does not work.
/**
* Listing 2-25. Example of Dynamicaaly Generated Methods That Are Created
* When a New Object is instantiated
*/
// Create a new user object that accepts an object of properties
function User(properties) {
// Iterate thorugh the properties of the object, and make sure
// that it's properly scoped (sas discussed previously)
var that = this;
for (var i in properties) {
(function() {
console.log("property: " + i);
// Create a nwe getter for the property
that["get" + i] = function() {
return properties[i];
};
// Create a new setter for the property
that["set" + i] = function(val) {
properties[i] = val;
};
})();
}
}
// Create a new user object instance and pass in an object of
// properties to seed it with
var user = new User({
name: "Bob",
age: 44
});
// Just note that the name property does not exists, as it's private within the
// properties object
alert(user.name == null);
// However, we're able to access its value using the new getnaem()
// method that was dynamically generated
console.log("name: " + user.getname()); // name: 44 :(
alert(user.getname() == "Bob");
// Finally, we can see that it's possible to set and gt the age using
// the newly generated functions
user.setage(22);
alert(user.getage() == 22);
Instead, after passing the i parameter as argument to the self-executing function,it works.
for (var i in properties) {
(function(prop) {
console.log("property: " + i);
// Create a nwe getter for the property
that["get" + prop] = function() {
return properties[prop];
};
// Create a new setter for the property
that["set" + prop] = function(val) {
properties[prop] = val;
};
})(i);
}
My question is:
- Why in the first case (for loop), it is not necessary to pass the i parameter, while
in the second (for in) it is needed in order to work properly?
It's because you're referencing
i
withinthat.get
andthat.set
in the second case, while in the first case, you're referencingitem
, which is invariant.In the first example, the self-executing function has access to the current value that the reference
i
points to (since it is executed right away), it makes a copy of the currentitem
withitem=item[i]
so that the inner function for the event handler, which will be called later, will reference the correct item.If you wouldn't do that the inner function, the event handler, since it doesn't execute right away would reference to the
i
in the top functions' scope; since thefor
will be long finished executing when you get to click the thing, it would reference to the last value ofi
most probably. By keeping the currentitem
withitem=item[i]
in the self executing function you get initem
the correct currentitem
for each one of them and so the event-handler can have access to the right value, the last value placed in each of the localitem
vars.In the first example you are declaring a copy of the contents of the array element in a local variable:
As the inline comment says, here we're remembering the value within the closure scope.
In the 2nd example, instead of passing
i
as a parameter, you could also have done:Which makes both examples more similar.
Similarly, you could also alter the first example to pass
i
as a parameter, rather than declaring it verbosely in a variable.It's arbitrary as to whether you declare a local copy of the variable using a
var
statement, or pass it as a parameter into your self executing function.Edit:
@Zecc bought up a good point in the comments, which I'd like to explain:
Where as:
This is because the
var variable = value
statement is effectively:and the
var
keyword always assigns the variable(s) following it with the valueundefined
.