2

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}
asked May 24, 2019 at 20:31
7
  • 2
    ‘i’ is not reference, how u expect 100 Commented May 24, 2019 at 20:35
  • @DennisVash I know it's not a reference, but my point was that it behaves differently from some other languages for instance try to run the same code in C#. Commented May 24, 2019 at 20:46
  • @DennisVash this is not exactly an issue about primitive vs. reference values (see Mark's reply below), so please consider deleting or editing your comments to make sure others don't get misled. Commented May 25, 2019 at 7:50
  • You use let in your JS, and var in your C++, and then wonder why they're different results? Commented May 25, 2019 at 9:50
  • @NiettheDarkAbsol I did not provide an example in C++ and if you are referring to the C# example, it uses an implicitly typed local variable that is very different from a JavaScript var. Commented May 25, 2019 at 9:55

1 Answer 1

3

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.

answered May 24, 2019 at 20:38
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.