This question is a few different (I think related) questions, which I give below, but broadly I am trying to understand a code piece from David Flanagan's O'Reilly book on JavaScript that gives an example as using a loop for creating closures as unintended:
function constfuncs() {
let funcs = [];
for(var i = 0; i < 10; i++) {
funcs[i] = () => i;
}
return funcs;
}
let funcs = constfuncs();
funcs[5]() //10, rather than 5
The above code example intends to have each element of the funcs
array be a function that can be called without arguments to return a value numerically equivalent to its index in funcs
; however, it does not succeed at doing this. I think I largely understand why this is: var i
, because it is declared using var
, is scoped to the function constfuncs
, rather than the block of that for
loop, and so hence it is the same i
being used by each function stored in funcs
, and i
ends as 10
in that loop, so hence any of the ten returned functions in funcs
will always evaluate to 10
when called.
On the next page of the book, it's stated that to fix the above, it's a one-line change: simply have i
be block-scoped, and so use let i
instead of var i
in that for
loop, as follows:
function constfuncs() {
let funcs = [];
for(let i = 0; i < 10; i++) {
funcs[i] = () => i;
}
return funcs;
}
let funcs = constfuncs();
funcs[5]() //is now 5, as desired
What I don't understand, however, is how this fixes the problem. i
to me seems to still exist only once, yet each of the ten created functions appears to have access to its own version/copy of i
in memory. If a new block-scoped local variable was declared in the for
loop that was assigned the value of i
on each iteration prior to then being used instead of i
as the value each function of funcs
returned, then perhaps it would make more sense to me, if it is the case that block-scoped local variables declared in a loop are redeclared (as using new memory) on each iteration; but even so, i
is not and yet the above still works as desired. This leads me to my questions:
- How are closures managed in memory in JavaScript? I am fairly new to JS and closures, and am used to languages having a stack and heap.
- Does the act of defining a closure cause a copying of memory from the stack to the heap to save outer local variables for use by that closure? This is because a closure seems to defy local variables only being stored on the stack, as it can still access them.
- Are block-scoped local variables re-created in memory (at a different memory location each time) for each iteration of an outer loop they are declared in?
- If possible yes to 3., is this influenced at all by whether a closure is used (ie, only created once in memory across loop iterations as the general case, but on each loop iteration if a closure has that local block variable in scope for it)?
- By "binding", do we mean a distinct location for a variable or constant in memory? I ask as the book states in its explanation for the above solution that "Because
let
andconst
are block scoped, each iteration of the loop defines a scope that is independent of the scopes for all other iterations, and each of these scopes has its own independent binding ofi
."
Questions 1-2 are related, as are 3-4; and all are related to the above example. Any other thoughts for explaining why the second approach above (with let i
) succeeds in fixing the solution are welcome.