19

I have seen anonymous functions inside for loops to induce new scope on the web in one or two places and would like to know if it makes sense.

for example:

var attr, colors = ['green','blue','red'];

for ( attr = 0; attr < colors.length; attr++) {
    (function() {
        var colorAttr = colors[attr];

        // do something with colorAttr
    })();
}

I understand it has something to do with keeping the scope inside the for loop clean, but in what situations would this be necessary? Would it be good practice to do this everywhere you need to declare a new var inside the for loop?

Evan
  • 5,975
  • 8
  • 34
  • 63
  • Here is an example where closure in loops make sense: http://stackoverflow.com/questions/1552941/variables-in-anonymous-functions-can-someone-explain-the-following – Salman A Dec 20 '12 at 17:05
  • 1
    `attr` changes as the loop progresses, so if you use `colors[attr]` in a callback function, it won't refer to the `colors[attr]` that you actually want. – Blender Dec 20 '12 at 17:05
  • I use async calls only when waiting for something to complete. In this case, it may be problematic to track the `colors[]` changes. – mkey Dec 20 '12 at 17:07
  • so does a pattern like this make more sense if you are attaching an event handler inside? – Evan Dec 20 '12 at 17:08
  • This isn't a callback - it is an immediately executing function. `colorAttr` is scoped to that immediately executing function, but it can walk up to the parent scope and happily access `attr`. – Fenton Dec 20 '12 at 17:09
  • so would it be bad or wrong practice to use this if there isn't a callback/event handler involved? – Evan Dec 20 '12 at 17:16
  • @Evan probably. Unless you are very paranoid about scope and really _really_ don't want `colorAttr` decalred in the same scope as `attr` and `color`. But in practice, that really shouldn't matter in well designed code. If you are not creating a function in a loop, you usually dont need to wrap it in a function like this. – Alex Wayne Dec 20 '12 at 17:32

2 Answers2

39

2021 Update

var used to be the only way to declare a variable. But we now have const and let which solve this problem in a better way. These variable declarations do respect the loop as a scope to bind to, which means the following snippet works fine and there is no need for an anonymous function to capture those values.

const colors = ['green', 'blue', 'red'];

for (let i = 0; i < colors.length; i++) {
    const color = colors[i];
    setTimeout(function() {
        alert(color);
    }, i * 1000);
}

What follows below is my original answer to this question from 2012.


When you have inner functions that are not executed immediately, as part of the loop.

var i, colors = ['green', 'blue', 'red'];

for (i = 0; i < colors.length; i++) {
    var color = colors[i];
    setTimeout(function() {
        alert(color);
    }, i * 1000);
}

// red
// red
// red

Even though var color is inside the loop, loops have no scope. You actually only have one variable that every loop iteration uses. So when the timeouts fire, they all use the same value, the last value set by the loop.

var i, colors = ['green', 'blue', 'red'];

for (i = 0; i < colors.length; i++) {
    (function(color) {
        setTimeout(function() {
            alert(color);
        }, i * 1000);
    })(colors[i]);
}

// green
// blue
// red

This one captures the value at each iteration into an argument to a function, which does create a scope. Now each function gets it's own version of a color variable which won't change when functions created within that loop are later executed.

Alex Wayne
  • 178,991
  • 47
  • 309
  • 337
3

You're almost there. It makes only sense in your snippet, if you pass in the attr value into your self invoking function as an argument. That way, it can store that variable within its very own scope object

(function( attr ) {
    var colorAttr = colors[attr];

    // do something with colorAttr
})( attr );

Now, the activation object respectively lexical environment record (which are ES3 and ES5 scope objects) will have an entry for whatever value is behind attr and therefore, its closured.

jAndy
  • 231,737
  • 57
  • 305
  • 359
  • "It makes only sense in your snippet, if you pass in the attr value into your self invoking function as an argument." the IIFE gets executed immediately, storing `colors[attr]` in `colorAttr` whether or not the `attr` value is passed in as a parameter. Certainly passing parameters is a clean approach. – zzzzBov Jul 07 '16 at 15:11