When I executed this code I expected to see 100 written in the console 10 times (based on experience from using other languages):
const arr = [];
for (let i = 0; i < 10; i++)
arr.push(() => i * i);
arr.forEach(f => console.log(f()));
But I get the square of each number (from 0 to 9). This seems to behave differently to some other languages.
For instance using C#, you will get 100 ten times:
var list = new List<Func<int>>();
for(var i = 0; i < 10; i++)
list.Add(() => i * i);
list.ForEach(f => Console.WriteLine(f()));
Python prints 81 ten times:
arr = []
for i in range(10):
arr.append(lambda: i * i)
for f in arr:
print(f())
Java doesn't allow this, as captured variables cannot mutate.
See this for examples in other languages.
As Mark explained below, when we use let (block scope) in JavaScript, the value of i is captured in a new scoped variable within each anonymous function. When you look at the actual definition of each function (using : arr.forEach(f => console.dir(f)); ), you'll see that there is a scope (_loop) from which i's value is captured in every iteration:
[[Scopes]]: Scopes[3]
0: Closure (_loop) {i: 0}
[[Scopes]]: Scopes[3]
0: Closure (_loop) {i: 1}
and so on...
If we re-run this example using var, unsurprisingly, the _loop block-level scope is missing and i is saved in the module/file level scope instead with the same value (10) for each iteration:
[[Scopes]]: Scopes[2]
0: Closure (./src/index.js) {i: 10}
1 Answer 1
This is the nature of using let to declare a variable in javascript — it's scoped to the (implied here) for loop block, which means each iteration gets a newly scoped name that is captured in each closure. The closures aren't all using the same name like they would be in some other languages, or in JS when using var to declare a variable.
To get your expected behavior, use var:
const arr = [];
for (var i = 0; i < 10; i++)
arr.push(() => i * i);
arr.forEach(f => console.log(f()));
The above code is the bane of many confused programmers which is one of the reasons it's nice to have the option of using let.
letin your JS, andvarin your C++, and then wonder why they're different results?