0

I have been stuck on this for a while. I had also threw in a debugger to see what is actually happening. I think it is something related to closure but not 100% sure.

var names = [ "roy", "nick" ];
var ids = [0, 1];
var people = [];

for ( i in names ){
    people.push({
        name: names[i],
        id: ids[i]
    })
    people[i].ages = {
        this_year: function(){
            var ages = [10, 11];
            return ages[i];
        }
    }
}
console.log(people[0].ages.this_year()); // why not 10

Tried using function arguments as suggest by @slebetman, however, i intend to invoke the function later so this wouldn't work..

var names = [ "roy", "nick" ];
var ids = [0, 1];
var people = [];

for ( i in names ){
    people.push({
        name: names[i],
        id: ids[i]
    })
    people[i].ages = {
        this_year: (function(x){
            var ages = [10, 11];
            return ages[x];
        })(i)
    }
}
console.log(people[0].ages.this_year); // 10
console.log(people[0].ages.this_year()); // not a function 
Yes
  • 63
  • 5
  • Yes, you've created a closure. All the functions you created in the for loop shares ONE SINGLE VARIABLE called `i`. And the value of `i` at the end of the loop is `1` – slebetman Aug 13 '16 at 08:32
  • @slebetman, yes, I can see that happening with the debugger, but why doesn't it create the function when i = 0, instead of creating the function at the end of the loop – Yes Aug 13 '16 at 08:36
  • if you were to fix the code so that it will return 10 for people[0].ages.this_year(), how would you fix it?? – Yes Aug 13 '16 at 08:39
  • Wrap it in another function. See my answer in the duplicate question. The ONLY way to break a closure in javascript is by passing values via function argument. – slebetman Aug 13 '16 at 08:40
  • see:http://stackoverflow.com/questions/3572480/please-explain-the-use-of-javascript-closures-in-loops#answer-3572616 – slebetman Aug 13 '16 at 08:41
  • I'm assuming this is an exercise ...`for ( i in names )` sets `i` to be each element of the `names` array in turn, not an index. Replace it with a more traditional `for (i = 0; i < names.length; i++)` loop. The closure thing use slebetman's approach or store the value of `i` in your ages object and use that to index `ages`. (i.e. `index: i, this_year ... return ages[this.index];` ... ` – Tibrogargan Aug 13 '16 at 08:48
  • @slebetman, I tried using the function arguments, while it does give me a return of 10 for people[0].ages.this_year, it isn't something I desire. I need the this_year to be a function where I can invoke later using people[0].ages.this_year(). – Yes Aug 13 '16 at 08:57
  • @Tibrogargan, sorry, I couldn't understand your third sentence. I can see how i is represented as an index in my original code and how i could write it using the traditional loop method. I don't understand the rest of your comment. Could you elaborate a bit more plz? Thx – Yes Aug 13 '16 at 09:00
  • `i` is NOT an index, it is an element value. `for ( i in names )` does not make `i` take the values `0, 1, 2 etc` it will literally take the values `"roy", "nick"` etc. Since both `names` and `ids` are arrays, neither of them have properties called `roy` or `nick` – Tibrogargan Aug 13 '16 at 09:04
  • @Tibrogargan, when I throw a debugger in the loop, it does show i is "0" to "1".. I am very confused now – Yes Aug 13 '16 at 09:08
  • 1
    Your `ages` property is an object that contains a single property called `this_year` that is a closure defined as `function() { var ages[10, 11]; return ages[i];` At no time in that function is the variable `i` declared, so it will not exist when the function runs (it will be undefined). You could add a second property to the object stored in `ages` that stored the value of `i` (i.e. `ages: { index: i, this_year: ... `. This would allow you to access it in the closure as `this.index`. It's odd, since you could just do `{ value: ages[i] }` - which is why I think it's an exercise – Tibrogargan Aug 13 '16 at 09:09
  • Ahh yeah, my bad. Too many new things in javascript. Yes, the `for .. in` construct on arrays iterates through each index as if it was a named property. – Tibrogargan Aug 13 '16 at 09:13
  • 1
    Probably should clarify some more. `i` does exist and will exist every time you access `this_year` simply because `this_year` creates a reference to it. The problem is that it's going to have the last value it took (11) every time you access `this_year`. It's defined, just not useful - every instance of `this_year` is going to have a reference to the same `i` – Tibrogargan Aug 13 '16 at 09:35
  • @Yes: See the duplicate question. You pass `i` as a function to a function that returns a function: `(function(z){return function(){return ages[z]}})(i)` – slebetman Aug 13 '16 at 10:54

0 Answers0