I've seen programmers assign events listeners inside loops, using the counter. I believe this is the syntax:
for(var i=0; i < someArray.length; i++){
someArray[i].onclick = (function(i){/* Some code using i */})(i);
}
Could someone please explain the logic behind this, and this weird syntax, I've never seen this:
(function(i))(i);
Many thanks for your time and patience.
I've seen programmers assign events listeners inside loops, using the counter. I believe this is the syntax:
for(var i=0; i < someArray.length; i++){
someArray[i].onclick = (function(i){/* Some code using i */})(i);
}
Could someone please explain the logic behind this, and this weird syntax, I've never seen this:
(function(i))(i);
Many thanks for your time and patience.
Share Improve this question edited Jan 23, 2011 at 21:54 Marcel Korpel 21.8k6 gold badges62 silver badges80 bronze badges asked Jan 23, 2011 at 21:52 DeanDean 8,0659 gold badges31 silver badges32 bronze badges 3- 3 This technically doesn't involve the closure. Although i'm not entirely sure what good it does; that inner function will run immediately, and unless it returns another function, the onclick will be set to something really odd. – cHao Commented Jan 23, 2011 at 22:02
- @cHao since this, immediate anonymous function call, is assign to onlick, it must return a function, so that returned function is a closure with "i" variable in surrounding environment. – jcubic Commented Jan 24, 2011 at 1:03
- @jcubic: If the code is indeed setting a DOM event handler, it must return a function if it's working correctly. There's not enough code here to justify either assumption. – cHao Commented Jan 24, 2011 at 1:19
2 Answers
Reset to default 7The (function(i))(i)
syntax creates an anonymous function and then immediately executes it.
Usually you'll do this to create a new function every time through the loop, that has its own copy of the variable instead of every event handler sharing the same variable.
So for example:
for(int i = 0; i < 10; i++)
buttons[i].click = function() { doFoo(i); };
Often catches people out, because no matter what button you click on, doFoo(10)
is called.
Whereas:
for(int i = 0; i < 10; i++)
buttons[i].click = (function(i){ return function() { doFoo(i); };)(i);
Creates a new instance of the inner function (with its own value of i
) for each iteration, and works as expected.
This is done because JavaScript only has function scope, not block scope. Hence, every variable you declare in a loop is in the function's scope and every closure you create has access to the very same variable.
So the only way to create a new scope is to call a function and that is what
(function(i){/* Some code using i */}(i))
is doing.
Note that your example misses an important part: The immediate function has to return another function which will be the click
handler:
someArray[i].onclick = (function(i){
return function() {
/* Some code using i */
}
}(i));
The immediate function is nothing special. It is somehow inlining function definition and function call. You can replace it by a normal function call:
function getClickHandler(i) {
return function() {
/* Some code using i */
}
}
for(var i=0; i < someArray.length; i++){
someArray[i].onclick = getClickHandler(i);
}