Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Please explain closures, or binding the loop counter to the function scope

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.

like image 658
Dean Avatar asked Jan 23 '11 21:01

Dean


People also ask

What is a closure and how does it relate to scope?

A closure is the combination of a function bundled together (enclosed) with references to its surrounding state (the lexical environment). In other words, a closure gives you access to an outer function's scope from an inner function.

How do closures work in JavaScript?

A Closure is a combination of a function enclosed with references to its surrounding state (the lexical environment). In JavaScript, closures are created every time a function is created at run time. In other words, a closure is just a fancy name for a function that remembers the external things used inside it.

Why function closure is important?

Closures are important because they control what is and isn't in scope in a particular function, along with which variables are shared between sibling functions in the same containing scope.

How would you use a closure to create a private counter?

3How would you use a closure to create a private counter? You can create a function within an outer function (a closure) that allows you to update a private variable but the variable wouldn't be accessible from outside the function without the use of a helper function.


2 Answers

The (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.

like image 136
Anon. Avatar answered Sep 27 '22 15:09

Anon.


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);
}
like image 21
Felix Kling Avatar answered Sep 27 '22 16:09

Felix Kling