apdm apdm - 2 months ago 4
Javascript Question

Why is this primitive variable being passed to a function by reference instead of by value?

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?

Answer

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);