1

This involves some Angular but I think it is mostly a pure javascript scope question. I have a directive that is invoked a couple times on my page with an ng-repeat. When it is first invoked, I set the variable current_index equal to the index number of the tag on the unit page like so:

var current_index = scope.$eval(attrs.index);

I then pass it to a function that is on a half second delay.

    g.append("g")
    .transition()
    .duration(500) //Half second delay
    .on("start", function(){
      tick(current_index);  //Passing the current_index variable
    });

However, by the time this function is called for each instance of the tag, current_index is always equal to the number of the last index that was invoked (Ex: If I have 3 tags corresponding to my directive, and print console.log(current_index); in my function that's on a delay, it will print 2 3 times, rather than 0, 1, 2.

function tick(index){
      console.log(index);
    }
 // Prints 2, 2, 2

However, if I introduce a new variable, a, and set it equal to current_index, and pass THAT into the function it works as expected.

var current_index = scope.$eval(attrs.index);
var a = current_index

g.append("g")
.transition()
.duration(500) //Half second delay
.on("start", function(){
  tick(a);  //Passing the a variable
});


function tick(index){
      console.log(index);
    }
 // Prints 0, 1, 2

It would seem that current_index is being passed as an object, and a is being passed as a primitive, but both return number when I do the typeof function. What's going on here?

apdm
  • 1,260
  • 1
  • 14
  • 33
  • 1
    It's passed by value in all of the cases you show, but you get the wrong behaviour because of scope. In the version with the `a` variable, do you really declare the `a`variable as shown on the line after and in the same scope as the `current_index` declaration? (See also [this question](http://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example).) – nnnnnn Sep 20 '16 at 03:15
  • thats scope issue. you need to wrap it in closure – YOU Sep 20 '16 at 03:15
  • I was able to follow the answer in the linked question but I still don't understand why introducing a new variable, only one line below, would be any different. I would think they would have the same scope and the new variable would also be wrong – apdm Sep 20 '16 at 03:30
  • Oh, the two variables really are declared right after each other like that? In that case I can't explain the behaviour you're seeing, unless there is some other code somewhere that updates the `current_index` variable. – nnnnnn Sep 20 '16 at 03:35
  • 2
    If `current_index` is a primitive value (like a number), then the only reason that assigning it to `a` and using `a` instead of `current_index` would make any difference is if some other code is changing `current_index` before you actually use the value. Otherwise, the two variables contain identical values and are in identical scopes. We would probably need to see a much larger context in the code to understand any other possibly more complex happening. – jfriend00 Sep 20 '16 at 03:39
  • You are *not* invoking that callback function, and you are not passing the variable `current_index`. Rather, you create the function and pass that, which **closes over** the variable by reference (regardless of the variable's value) – Bergi Sep 21 '16 at 00:03

1 Answers1

1

This can be confusing. But remember, you're not passing current_index here, you're defining a function, which has a closure, and via that closure, has access to the variable current_index, in the parent scope.

.on("start", function(){
  tick(current_index); // you have access to this because of the closure
});

So if you change the value of current_index, whatever current_index is when that anonymous function you created there executes, that's what will be passed to tick().

This is one of the reasons why creating functions inside of for loops is dangerous. You'll need to either use an immediately invoked function that takes current index as an arg, and returns the function you want .on to execute. Or you can bind the function you are passing:

.on("start", function(boundIndex){
  tick(boundIndex); // now the value at the time of binding is bound
}.bind(null, current_index);
Cooper Buckingham
  • 2,503
  • 2
  • 15
  • 23