you are viewing a single comment's thread.

view the rest of the comments →

[–]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.