0

I am building several functions to be triggered when the user clicks over a link. The problem is that I use a loop to reduce code, but that loop uses a local variable. This variable is lost and appears (logically) as undefined when the user clicks the link -> executes the function.

I can't see any way to avoid that apart from explicitly using real numbers and no variables, but that makes the code really unsustainable. Am I missing some hidden options here?

for (var i=0; i<graphs.length; i++) {
    for (var j=0; j<combinations.length; j++) {
        graph[i].label[j] = function() {return this.x + ' text ' + combinations[j].name;}
    }
}

This returns the logical 'combinations[j] is undefined' error. I can do the following

for (var i=0; i<graphs.length; i++) {
    graph[i].label[0] = function() {return this.x + ' text ' + combinations[0].name;}
    graph[i].label[1] = function() {return this.x + ' text ' + combinations[1].name;}
    graph[i].label[2] = function() {return this.x + ' text ' + combinations[2].name;}
    graph[i].label[3] = function() {return this.x + ' text ' + combinations[3].name;}
    graph[i].label[4] = function() {return this.x + ' text ' + combinations[4].name;}
}

And it works since all these variables are global (let's say they are accessible through a global variable) (except the previous loop-vars i and j)

Any idea? Cheers

UPDATE: As it turns out, I have solved it just by declaring another variable, maybe it works because it is created in every iteration? I ignore it, but it works as it should (perhaps because of how highcharts.js internally works)

for (var i=0; i<graphs.length; i++) {
    for (var j=0; j<combinations.length; j++) {
        var workaround1 = i;
        var workaround2 = j;
        graph[i].label[j] = function() {return this.x + ' text ' + combinations[workaround2 ].name;}
    }
}

UPDATE2: No, it doesn't help as the variable stores the last value and this is the one retrieved. The problem still persists.

UPDATE3: Finally, and solved already here How do I pass the value (not the reference) of a JS variable to a function?, I have adapted the solution to mine

for (var i=0; i<graphs.length; i++) {
    for (var j=0; j<combinations.length; j++) {
        (function(g) {
        graph[i].label[j] = function() {return this.x + ' text ' + combinations[j].name;}
        })(g);
    }
}
Cœur
  • 37,241
  • 25
  • 195
  • 267
GWorking
  • 4,011
  • 10
  • 49
  • 90

2 Answers2

2

When you're using a function expression within a for block you have to pass in the iteration variable (i and j in this case). This tells the function expression the exact value without letting it be disposed

for (var i=0; i<graphs.length; i++) {
   for (var j=0; j<combinations.length; j++) {
      graph[i].label[j] = function(j) {return this.x + ' text ' + combinations[j].name;}
                                // ^ pass it into the function
   }
}

In response to comment: Yes you can do 2 variables. Here is a rough mockup

var graph = [1, 2, 3, 4, 5];
var combin = ['a', 'b', 'c', 'd'];

for (var i = 0; i < graph.length; i++) {
  for (var j = 0; j < combin.length; j++) {
    (function(x, y) {
      console.log('[' + x.toString() + ',' + y.toString() + '] =' + graph[x] + combin[y]);
    })(i, j);
  }
}
jasonscript
  • 6,039
  • 3
  • 28
  • 43
1

this seems to work for me when passing j as argument

graph = [{"label":["foo","dsa"]},{"label":["foo","dsa"]},{"label":["foo","dsa"]},{"label":["foo","dsa"]}];
combinations = [{"name":"foo"},{"name":"bar"},{"name":"boo"},{"name":"far"}];


for (var i=0; i<graph.length; i++) {
for (var j=0; j<combinations.length; j++) {
    graph[i].label[j] = function(j) {return this.x + ' text ' + combinations[j].name;}
}}

console.dir(graph);
john Smith
  • 17,409
  • 11
  • 76
  • 117