So I am trying to figure out this puzzle:
function fun1(){
var result = [];
for (var i = 0; i < 5; i++){
result.push( function() {return i} );
}
return result;
}
console.log(fun1()[0]()) // returns 5?
Shouldn't the first element of that array return a function that returns '0'?
You should read a little about Javascript closures, look it up.
The variable
i
from thefun1
function is accessible from the function in the loop, but it's the same variable, not a clone.The value of "i" in the inner function will be the value of "i" in the outer function when it returns. So, all 5 elements in the array will return "5" when called.
No, it should return 5.
The function still have the reference to i, which is 5 after for.
This happens because your function (closure) maintains a
i
rather than
i
as it existed during each particular iteration.This means that your function, when executed, will know the current value of
i
at that instant. Because the loop will already have finished at that point, that will be the last value ofi
set by the loop. So, how do we get the snapshot value instead of the reference? Luckily, numeric parameters are passed by value... so, you can avoid your issue by passingi
to a second function...(fiddle):[Edit] Let's take the loop out of the equation (fiddle):
So, to "fix" this (fiddle):
What a question!
The problem is, in javascript, as in java, integer values are passed by value, not by reference. Which is what everyone else here is telling you. But i'm assuming you don't know what this means. It is pretty standard across languages, so let's see if i can explain!
NOTE: this is a crippled explanation. I don't really know how javascript is passing the values, and fun terms like "stack" and "heap" and "pass by reference value" (which i think is technically correct?) can pop up in more academic answers. Hopefully i can skip all that, and assume that
So, we have pass-by-reference and pass-by-value. In the program's memory, when passing by value, the thing that the variable "i" is pointing to is a literal number. When you pass by reference, the thing that the variable "i" would point to would be the memory location of some other object.
What does this mean?
after line 1. the program will make a little home in memory for "i", and it will give that a locatiion - i don't know, some ram value (say. i don't really know the under-the-hood javascript). Then, in that value, it will put another value. So we have:
so the program knows that, when it sees the value "i", it goes to memory location 0x67, and gets the value 0x68. It also knows that THAT value is pointing to another memory location. In which case we have
After line 2. runs, we'd have
and
to give:
Now, as it happens, if you were to so an equality on the two new Ojbects, you'd fine they're the same ( i think ). What is important to note here, is that after the second line, the value of i has changed, from the first memory location, to the second.
SO, if you did this:
you would get:
See? "j" gets the actual byte values in "i", which means "j" gets the value 0x68, which is the memory location for a new object. After the third line, j has the location of the first object, and "i" has the location of the second object.
HOWEVER, any changes you make to j - say
won't appear in the object that "i" is pointing to. It will only appear in "j" object. Because, remember, j is pointing to a totally different object in a totally different location from the object i is pointing to.
Ok, with that out of the way, let's swing back to pass-by-value.
Here
so we get
AND, if we were to look at j?
so, because we updated "i", we do not also update "j", get it? The bytes are being held, j get its own copy of the bytes here.
OKAY, so with all the above out of the way, let's check out your code:
right, so in the for loop, you are making a function that holds a reference to i, and the action of the for loop is to increment i.
You're probably going "hold up!" or something - do you think internally like that? - because i just noted how the data is passed by value for integers (or numbers, generally). Problem here is this:
From this page: https://developer.mozilla.org/en/JavaScript/Guide/Closures
So... and i pasted the above for a reason - a closure is an object that is a function (fun1, say) and the environment in which the function was created. And the environment? It consists of the local variables (ooh, how about i?) in scope.
So, to be blunt... just as i is still in scope the entire time of that for loop, that same "i" is in scope for each and every closure we generate. And that i? that i is getting its value updated. You're not passing the snapshot of i, you're passing, as near as i can determine, the reference object itself. It is no longer pass-by-value, we're kinda sorta back to pass by reference.
So, in memory, we have
in your array, you're putting a function that just spits back the value of i. But you're incrementing i all the time. Each function you put in that array is going to spit out "5".
To take it one step further...
Here, we're testing to see if we can "keep" changes outside the initial environment. If you run this, you won't see any changes stored. In the first example, we increment "i" to 6 - but when we run the example again, i is again 5 - the increment was not kept.
Finally, trying on another array function shows the value wasn't incremented there either.
So, for you, what you want is to somehow freeze in time the value of i at the moment it enters the array
And that's pretty tricky.
Where "eval" is a fancy javascript function that creates code to be run at the time it is run - dynamically, i mean. And i surround it by parenthesis because
Why does JavaScript's eval need parentheses to eval JSON data?
I don't know a super amount about eval though!
Next up, one of the other solutions shows this:
which is placed within the context of the for loop - to give
What this does is create a new environment for the closure to exist within. And in this environment, "x" is fixed forever and ever as the value that i currently (ie at point of instantiation) is. So when our array tries to access a given closure, it exists within a new environment where x is fixed. If we'd done this instead:
Then we'd run into the same problem. The "i" still exists in the environment (or scope, if you will), of the loop, so it is going to get picked up, no matter how many times you stick it in functions.
If anyone reads this and thinks what i've written is bollocks, please let me know! I'm kinda keen to hear how js does memory management stuff.
Let's break down what happens step by step:
We declare a function
fun1()
that returns an arrayresult
.The
for
loop iterates 5 times, with each iteration incrementingi
.Notice that the anonymous function returning
i
is not invoked throughout the execution.The
for
loop ends with the value ofi
being5
.Invoking
fun1()[0]
returns an arrayresult[]
which has a function that stores the reference toi
, not the value ofi
.Invoking the anonymous function then follows that reference and returns the value of
i
, which is5
.