I have a problem getting the value of 'name' displayed with the following:
for (var name in array) {
var hoverIn = function() {
alert(name);
};
var hoverOut = function() {
};
thing.hover(hoverIn, hoverOut);
}
What I get is an alert window with the last value of name. Clearly I am doing something wrong and I suspect it's a simple fix. Can anyone help?
Thanks.
4 Answers 4
It's closure problem, name, after that iteration is the last name in array, and callback for hovers isn't executed right away when the iteration happens, thus when the hover function is actually executed, name will always be the last in array.
You need to use IEFE (Immediately executed function expression):
for (var name in array) {
// pass in name to the anonymous function, and immediately
// executes it to retain name when the particular iteration happens
var hoverIn = (function(name) {
return function() {
alert(name);
}
})(name); // if you notice, the pattern is (function(name) {})(name)
// the first () creates an anonymous function, the (name)
// executes it, effectively passing name to the anon fn
var hoverOut = (function(name) {
// same pattern if you need to re-use name inside here, otherwise just
// function() { } should suffice
})(name);
thing.hover(hoverIn, hoverOut);
}
To avoid duplicates of (function() { })() (which honestly is getting tiring to look at), you could also, as @pimvdb pointed out, wrap the whole body in a closure:
for (var name in array) {
(function(name) {
var hoverIn = function() {
alert(name);
}
var hoverOut = function() {
}
thing.hover(hoverIn, hoverOut);
})(name); // closure of for loop body
}
2 Comments
for loop body.Add a variable inside the loop
var thisName = name;
and then use that in your function
alert(thisName);
Comments
You have two ways of dealing with this problem.
The first thing to know is that scope only happens at function level, not within loops in javascript. If you set a variable within a function from an outside source and don't execute it right away,the variable will be changed over the course of your loop.
You can solve this by closing other the variable:
var names = ["john","paul","george","ringo"];
var store = {};
//this function receives the data as a parameter
//so it will be a safe copy.
function createFunc(name){
//just return a function that will alert the name.
return function(){
alert(name);
}
}
for (var i in names) {
var hoverIn = createFunc(names[i]);
store[names[i]]=hoverIn;
}
store["john"]();
The other way is to create an anonymous function that executes right away within the loop:
var names = ["john","paul","george","ringo"];
var store = {};
for (var i in names) {
//the function receives the i as a parameter
//and executes, so n is a safe copy of i
(function(n){
var hoverIn = function(){
alert(names[n]);
}
store[names[n]]=hoverIn;
})(i);
}
store["john"]();
Everything is a problem related to closure. Look at wikipedia for more info.
Comments
You should create closure:
for (var name in array) {
var hoverIn = (function() {
return function() {
alert(name);
};
}());
var hoverOut = function() {
};
thing.hover(hoverIn, hoverOut);
}
8 Comments
name still refers to the one of the for loop, so it doesn't really change anything.setTimeout example of your demo: jsfiddle.net/yHSpg/1.1 twice, not 0 and 1.