3

I know that when I declare a let variable in a loop the variable is declared in each cycle. For instance:

for(let i=0 ; i<3 ; i++)
{
    let x=1;
}

The variable x is declared 3 times namely are allocated three different places in the memory for each variable in the loop.

However what does it happen to the variable i? Is it declared once? Or is it initialized one time alone but is declared three times too?

I would like to understand what it happens behind the scene.

Nick
  • 1,439
  • 2
  • 15
  • 28
  • `i` is declared once, `x` is declared again, but don't worry about it.. Internally the V8 engine, or whatever engine you browser is using, will optimise `x`.. – Keith Jan 29 '20 at 12:24
  • Also local to the loop's block's environments, aka also three variable creations for `i` in this case. – ASDFGerte Jan 29 '20 at 12:26
  • Note: not directly a dupe of, but very related - [javascript-closure-inside-loops-simple-practical-example](https://stackoverflow.com/questions/750486/javascript-closure-inside-loops-simple-practical-example) – ASDFGerte Jan 29 '20 at 12:31

2 Answers2

3

With let, i is declared once and gets a fresh binding for every cycle. And its scope is only visible inside of the block statement.

From here Variables and scoping in ECMAScript 6:

let in loop heads

In loops, you get a fresh binding for each iteration if you let-declare a variable. The loops that allow you to do so are: for, for-in and for-of.

for (let i = 0; i < 3; i++) {
    let x = 1;
    setTimeout(() => console.log(i), 1000);
}

Another example with an assignment for having a fresh binding.

for (let x = { v: 0 }; x.v < 2; x.v += 1) {
    setTimeout(() => console.log(x), 100); // same values
}

for (let x = { v: 0 }; x.v < 2; x = { v: x.v + 1}) {
    setTimeout(() => console.log(x), 100); // different values
}
.as-console-wrapper { max-height: 100% !important; top: 0; }
Community
  • 1
  • 1
Nina Scholz
  • 376,160
  • 25
  • 347
  • 392
  • Are you sure?, what you have created here with your `setTimeout` is a closure, and from what I can gather that will be only created when required, if `i` is not used this way, then no closure is required. I'd be very surprised if V8's engine created a closure when one wasn't required. – Keith Jan 29 '20 at 12:32
  • Thus, i is declared again and initialized again with the value in the "increment section (i++)". – Nick Jan 29 '20 at 12:32
  • Why is the `i` declaration executed multiple times ? According to https://www.w3schools.com/js/js_loop_for.asp the first statement "is executed (one time) before the execution of the code block." – Mickael B. Jan 29 '20 at 12:32
  • 1
    I thought you could test this using the comma operator (something like [this](https://jsfiddle.net/gfj8z06v/) maybe), it says it is initialized once? – Nick Parsons Jan 29 '20 at 12:35
  • 1
    @MickaelB. Please do not use w3schools as reference for anything that goes beyond the basics of the basics. See [meta topic](https://meta.stackoverflow.com/questions/280478/why-not-w3schools-com). They are not a good source. – ASDFGerte Jan 29 '20 at 12:37
  • `i` is always new for each cycle. it is called *fresh binding*. – Nina Scholz Jan 29 '20 at 12:39
  • 1
    I'm not sure that's true, otherwise this wouldn't work -> `for (let x={v:0}; x.v < 10; x.v += 1) { console.log(x);}` unless maybe the browser is making it into a `const`,.. Anyway, seems very wasteful doing a copy, when not required. – Keith Jan 29 '20 at 12:41
  • 1
    @NickParsons the value is copied over, the initialization is not done again, see [13.7.4.9 Runtime Semantics: CreatePerIterationEnvironment](https://tc39.es/ecma262/#sec-createperiterationenvironment) – ASDFGerte Jan 29 '20 at 12:41
  • @ASDFGerte Didn't knew thanks. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for says the same thing that the initialization is "evaluated once before the loop begins" – Mickael B. Jan 29 '20 at 12:43
  • @Keith "*from what I can gather that will be only created when required*" - engine optimisations don't change how things work in general. You could also say that the `let x = 1` does not create a variable (because `x` is never used and optimised out) although it arguably does. – Bergi Jan 29 '20 at 12:44
  • @Bergi So is `for (let x={v:0};` doing a full copy of the object on each iteration?. Or is the `{v:0}` is static in memory, and what's happening here is that a new `x` is getting re-assigned to the old `{v:0}`. The reason I also believe closures are created when required, do 2 setTimeouts with different wait times, you will still get `1,2,3` implying there was 2 closures created, not just one. So making the closure a the block level of the let definition only would fail. – Keith Jan 29 '20 at 12:50
  • @Keith, it is a problem of the assignment. if directly a new scope is created and if just the same value is used, then it shares the same value. – Nina Scholz Jan 29 '20 at 12:52
  • I'm a bit confused, doesn't your `same values` prove here that `let x={v:0}` is indeed NOT getting a fresh binding. It's certainly running like it's not!!,. with the result been `2 2`, it appears to be the same bindings. IOW: x is the same x that was used at the start of the `for` and the subsequent setTimeouts. – Keith Jan 29 '20 at 13:15
  • in the first example `x` keeps it old value (only a property changes). In the second `x` gets a new assignment. – Nina Scholz Jan 29 '20 at 13:18
  • @Keith Objects are never "fully copied". An object is a reference value, and when you do `x_1 = x_0` then nothing but the reference is copied into the other variable. – Bergi Jan 29 '20 at 13:36
  • First of all thanks for the answers. But I'm still a bit confused. When i declare a variable with let in the body of a for (for-in and for-of too) happens like with a call function namely a new execution context is created with its own variable object and scope chain thus the x variable is redeclared (like if was redeclared). And the same should be for let i in the initialization of the for, rather @NinaScholz said that i is declared once. Furthemore I don't understand why in the first case of the second example you get the same value; being x declared with let I would have expected 0 1 too. – Nick Jan 29 '20 at 15:06
  • @Umbert, x has never changed with an direct assignment. the result is like using `var`. – Nina Scholz Jan 29 '20 at 15:14
  • @NinaScholz I saw that. Therefore each iteration require a new LexicalBinding only if there is a fresh value for the variable declared with let otherwise no. Can you confirm? Sorry in the case I had said a nonsense – Nick Jan 29 '20 at 15:24
  • 1
    right, exactly. – Nina Scholz Jan 29 '20 at 15:25
0

i is declared every cycle if you use let.

i is declared once if you use var.

i is not declared every cycle if you use const (it has the same scope as the loop). Try this and you will get an error:

for (const i = 0; i < 9; i++) {
   console.log(i);
   // more statements
}

error: Uncaught TypeError: Assignment to constant variable.

Reference here.

leosbrf
  • 159
  • 2
  • 15