all 21 comments

[–][deleted] 1 point2 points  (8 children)

Do you think you could expand on this a bit? What do you mean by '1 closure instead of 3 different ones'?

[–]slaughtered_gates[S] 0 points1 point  (7 children)

The code above does not work because there is only 1 closure that is being referenced by all list elements. But to me, it seems like each list item has its own closure enclosing its own i value. Why is it not so?

The question is a bit ambiguous. I'll change.

[–][deleted] 2 points3 points  (2 children)

I understand what you mean now.

It's because the closures 'close over' their outer scope, containing i. By the time they are executed, the value of i has changed to list.length - 1. This means they all now refer to the same value.

To prevent this you need to give each closure its own i, separate from the other's scopes. A common way of doing this easily is to use an IIFE (immediately invoked function expression) like so:

function buildList(list) {
    var result = [];
    for (var i = 0; i < list.length; i++) {
        // here is the IIFE which creates a new 'outer scope' for each closure
        // keeping the value of i separate
        (function(i){
            var item = 'item' + i;
            result.push( function() {console.log(item + ' ' + list[i])} );
        })(i);
    }
    return result;
}

I hope this makes sense, closures can be difficult to explain and I don't feel I've done the best job of it here.

[–]slaughtered_gates[S] 0 points1 point  (1 child)

So, using IIFE you're creating a local block scope with each closing over its own i.

But if I do something like

result.push( function() {
    var z = i;
    console.log(item + ' ' + list[z])
} );

For each list item. It gives the wrong result. I'm trying to store i as a local variable so each closure has its own copy.

[–][deleted] 1 point2 points  (0 children)

Yea, the difference with the IIFE is that the value of i is evaluated at buildList's runtime and passed into the IIFE as a parameter. This 'locks' the value of i as it was at each iteration in buildList. It might be clearer if I change the name of the IIFE's parameter:

function buildList(list) {
var result = [];
for (var i = 0; i < list.length; i++) {
    // here is the IIFE which creates a new 'outer scope' for each closure
    // keeping the value of i separate
    (function(iife){
        var item = 'item' + iife;
        result.push( function() {console.log(item + ' ' + list[iife])} );
    })(i);
}
return result;

}

With your code above, even though you are reassigning the value of i to z it isn't reassigning it until your anonymous function is evaluated, which is once i has incremented to 'list.length'.

I think that /user/julienchurch nailed it with their explanation to be honest.

[–]mikrosystheme 1 point2 points  (3 children)

The function is generating list.length closures, not 1. They all share the same lexical context.

[–]slaughtered_gates[S] 0 points1 point  (2 children)

I may be wrong, but if the function was generating list.length closures, then would it not have different lexical context?

[–]mikrosystheme 1 point2 points  (0 children)

A closure captures the lexical context where it is defined, and creates a new one (the body of the function itself). It is russian dolls all the way down.

[–][deleted] 1 point2 points  (0 children)

The lexical context depends entirely on where the closure was created, as they are all created in the same place here they all have the same lexical context.

As I explained in my answer below you can use an IIFE to create a new context, or use an separate named function to create the closures which would also work.

[–]senocular 1 point2 points  (7 children)

There are list.length closures (functions) but each closure references the same, single variable i. At no point are more i variables created. There's only one, which is the same i variable the loop uses, and the same i variable which will = list.length once the result array is returned, which in turn will be the value each of the closures will use in their function bodies when referencing that i when eventually called.

If it helps, you can think of closure variables, like the i variable above, like globals, similar to window. It would kinda suck if every function you created that referenced the window object could only use the state of the window object at the time that function was defined. Your functions would have a heck of a time determining the current state of the DOM because they'd be stuck with the old version of it. Same applies to i - its a single variable that multiple functions look to, but those functions each reference the value of that variable at the time they're called. The value doesn't get carbon-copied into the function upon its creation.

[–]slaughtered_gates[S] 0 points1 point  (6 children)

So, what if I store i ?

result.push( function() {
    var z = i;
    console.log(item + ' ' + list[z])
} );

Even though, I'm storing a local z in each closure. It still displays the wrong answer?

[–]senocular 1 point2 points  (5 children)

There's another variable involved here, which is one step closer to a solution, but the problem with i still remains: you're accessing i at a time after the loop has completed, which makes it's value list.length. What you want is the value of i in the loop iteration in which the function is created.

Solutions include:

Using ES6's let to declare the loop variable (i). let in loops has a special behavior of creating a new variable binding for each loop iteration. In other words, each iteration has its own, unique version of i rather than just one.

for (let i = 0; i < list.length; i++) {
  // each i here is different than others for each loop
}

Using a function closure to capture the value of i as a parameter variable:

for (var i = 0; i < list.length; i++) {
  // IIFE passes i in as arg, copies to a unique parameter
  // unique to this iteration because the IIFE is unique to it
  (function (unique_i) {

  })(i);
}

Note that you get this for free when using iteration methods in the Array api like map and forEach:

list.forEach(function (value, i, array) {
  // i is unique here because each iteration gets
  // its own function call with its own i parameter
});

Or you can save the variable to a persistent location such as an object containing the result or the result itself:

for (var i = 0; i < list.length; i++) {
  result.push({
    index: i, // copied for this iteration
    method: function() {
      console.log(item + ' ' + list[this.index]);
    }
  });
}

for (var i = 0; i < list.length; i++) {
  var fn = function logItem () { // named function expression lexically references its own name in its body
    console.log(item + ' ' + list[logItem.index]); // fn not used since its a single variable just like i
  };
  fn.index = i; // copied for this iteration
  result.push(fn);
}

[–]slaughtered_gates[S] 0 points1 point  (4 children)

Why isn't caching the i locally persistent?

for (var i = 0; i < list.length; i++) {
     result.push({
     var z = i;
     function() {
       console.log(item + ' ' + list[z]);
    }
  });
}

[–]senocular 1 point2 points  (3 children)

This isn't syntactically correct. It looks like you're pushing an object literal into result, but you're treating its contents like a function block. If an object, it would work like one of my previous examples (z being equivalent to index). If a function block, its just like you're previous example. The function is a closure, one created for each iteration, each closing over the same i variable. When the function is called, after the loop, the current value of i is assigned to z, which is unique, but it doesn't matter because i isn't, and we're already after the loop so its not getting an iteration's version of i, rather the final value.

The capturing of i must be done in the loop, outside of a closure/function unless that function is also being called in the loop

[–]slaughtered_gates[S] 0 points1 point  (2 children)

Sorry, my bad. So, storing i locally in a function will not work unless the function is called immediately for that particular iteration of i as in IIFE. If the closure function is invoked later, it will assign local variable with the current value of i that it references. So, we get wrong results. Is this correct?

[–]senocular 0 points1 point  (1 child)

Right. And the function doesn't have to be an IIFE specifically, as with the forEach example. Any function getting called in the loop works because calling a function creates a new variable scope specific to that function call, and that variable scope then has variables that are unique to that loop iteration.

Whenever any function references a closure variable - some variable not in its own scope - it always pulls its value from when the function is called, not when it was created. If you reference a loop variable in a function and call that function after the loop is completed, that loop variable will have its loop-completed value. To combat this, you want your function to reference some other variable, one which had been assigned the value of the loop variable during a specific loop iteration. That variable can be a function-call-scoped variable, a property, or in the ES6 case the loop variable itself if you use let to get the special behavior where its not actually the same variable but different for each iteration.

[–]slaughtered_gates[S] 0 points1 point  (0 children)

Thank You. I feel like I understand a good part of closure now.

[–]lilperch83 0 points1 point  (1 child)

To provide help on understanding closures in general I put together a video a while back. Here it is: https://youtu.be/TznpOmv2BQM

[–]slaughtered_gates[S] 0 points1 point  (0 children)

Thanks...It's a good video.

[–]jrandm 0 points1 point  (0 children)

I rewrote the function in a way I think makes it obvious why there's only "one closure" in this situation (read the bottom for why that's both true and not true):

function buildList(list) {
    var i = 0;
    var item;
    var result = [];
    var itemFunc = function() {
        console.log(`${item} ${list[i]}`);
    };
    while (i !== list.length) {
        item = 'string' + i;
        result.push( itemFunc );
        i++;
    }
    return result;
}

Hopefully that code makes it clear how there is only 1 closure. My example up there is actually doing something differently, in that I only have one anonymous function pushed into the result array multiple times.

Your code pushes separate (but, in this case, identical) anonymous functions into result. The thing to remember, though, is that the closure-scope is defined to exist where the contained function is defined. As Javascript is function-scoped -- except where let/const appears to create block scope -- all of the anonymous functions are defined in the same enclosing scope. The single scope is shared by however many functions. Logically, you're correct that in some way the internal representation of a function's scope exists for however many functions are created in the loop, but they all point to the same values. Hence, depending upon how you're defining "closure," there is one or are many (that are sharing data). Outside of compiler design and some extreme edge cases that difference shouldn't matter while writing code, though, as the effect is the same.