I have understood ,why the output of this code should be 3 3 3.
for (var i = 0; i < 3; i ) {
setTimeout(() => console.log(i), 1);
}
I am not able to understand , why the output of this code is 0 1 2
for (let i = 0; i < 3; i ) {
setTimeout(() => console.log(i), 1);
}
I want more clarity with the output of the second for loop.
CodePudding user response:
There is a difference of the scope with let
and var
, which causes them to behave differently.
See this other Stack Overflow answer:
The main difference is scoping rules. Variables declared by
var
keyword are scoped to the immediate function body (hence the function scope) whilelet
variables are scoped to the immediate enclosing block denoted by{ }
(hence the block scope).
So, technically, var
is referring to the same variable address. But, as let
is blocked scope (according to the quote above), every callback in setTimeout()
will make i
have a different value then the previous one.
Thanks to Nick Vu for the example!
Another experiment that is possible is to make let
behave like var
. To do this, use the code below.
let i;
for (i = 0; i < 3; i ) {
setTimeout(() => console.log(i), 1);
}
As we moved let
to the main scope, it behaves like var
(as var
is positioned at the top of the file with a value of undefined
at runtime).
This makes the let
variable behave like var
, making the output of the for
loop the same as if the let
variable was var
.
A quick overview of var
hoisting below.
// Actual code:
web = "stackoverflow.com";
var web;
// Is understood as...
var web;
web = "stackoverflow.com";
Basically, defining a variable with var
anywhere will always result it being at the top of the file, resulting in the weird behaviour with the for
loop.
This is why many people prefer let
over var
; because of hoisting!
CodePudding user response:
The first loop var i
is hoisted and always referred to only 1 variable during that loop, setTimeout
will postpone your console.log
with call stack, so that's why the value is 3 all.
But let i
is different, i
value will be initialized under that block scope in iteration each time, so that's why the result is 0 1 2
For an experiment, to make let
has a similar result as the var
loop
let i; //move `i` to the upper block scope
for (i = 0; i < 3; i ) {
setTimeout(() => console.log(i), 1); //all logs will share the same `i`
}
CodePudding user response:
In case of var
you are always referring to the same variable address, because var
is function scoped.
let
is block scoped. For every callback in setTimeout you have a different value of i
, since it is a different block.
CodePudding user response:
This is too lengthy but I make sure that you get it,
In the first for loop
, variable is declared using ‘var’
keyword. Variables declared using ‘var’ keyword are function scoped. As our first ‘for’ loop is not enclosed
within any function, the variable is global
, and the value of this global variable
was incremented
at each iteration of the first for loop. At the end of the first for loop, the variable ‘i’
will have the value of 3
as the value is overwritten
at each iteration of the first for loop. So, at the time of execution of the first callback function from the ‘callback queue’
, the value of ‘i’ will be 3.
So, the first callback function will print numeric value 3 at console. The second and third callback function will also print the same numeric value 3 as it points the same variable.
But in the case of second for loop, we’ve used ‘let’
keyword to declare the variable. Variables declared using ‘let’ keyword are block scoped
. A block is nothing but a pair of curly braces. So, in second for loop, a new value is used for each iteration. So, the last 3 callback functions will have the numeric values 0, 1, 2 respectively for the variable ‘i’ and hence would print 0, 1 and 2.
We can make the callback function to use the new variable value every time using ‘var’ keyword itself. As variables in javascript are function scoped (unless you use ‘let’
or ‘const’
keyword), you can call a function by passing the iteration variable. This will create a new variable every time.
CodePudding user response:
for(var i=0 ;i <3 ; i ){
setTimeOut(()=>console.log(i),1);
}
Js engine will examine the first for loop in the following steps :
- i will be hoisted, to look like this :
var i;
for(i = 0 ;i < 3 ; i ){
setTimeOut(()=>console.log(i),1);
}
- After the loop get's over , it will run setTimeOut for 3 times which will print value of i which is now 3
{
i = 3
setTimeOut(()=>console.log(i,1);
}
{
i = 3
setTimeOut(()=>console.log(i,1);
}
{
i = 3
setTimeOut(()=>console.log(i,1);
}
for(let i=0 ;i <3 ; i ){
setTimeOut(()=>console.log(i),1);
}
Js engine will examine the second for loop like this :
- i will be hoisted, to look like this :
for(let i=0 ;i < 3 ; i ){
setTimeOut(()=>console.log(i),1);
}
for the first iteration , the value of i would be 0 , and for the second iteration the value of i would be 1 and for the third it's value would be 2 .
Imagine , it as three different blocks of scope like this :
{
i = 0
setTimeOut(()=>console.log(i,1);
}
{
i = 1
setTimeOut(()=>console.log(i,1);
}
{
i = 2
setTimeOut(()=>console.log(i,1);
}
CodePudding user response:
First loop
for (var i = 0; i < 3; i ) {
setTimeout(() => console.log(i), 1);
}
Second loop
for (let i = 0; i < 3; i ) {
setTimeout(() => console.log(i), 1);
}
Solution of first loop
Because when we declare the variable using var it will be either in global scope(when declare in global execution context) or in function scope(when declare in function execution context).So as we know in the first loop we declare the variable in the global execution context so it will have global scope. When setTimeout encounters in first loop it will give the setTimeout handler in the API's. But as we know js and time never waits for anything so js will go to next iterations and js engine will execute the for loop very fastly and when the 1s of setTimeout happens then it will push the handler functions in execution stack and now the value of i(global scope) is 3. Here the main thing is that the same i is bound to every function of setTimeout because of the concept of closures(if beginner then to understand think that i is a global variable). So console.log(i) will print 3 in each handler function of setTimeout.
Solution of second loop
Till ES6 developers feels very difficult because there is no block scope in javascript like other languages. So in ES6, keyword let is introduced which introduces the concept of block scope. Means the scope of variables declared using let will be only in that block(variable enclosed in the first curly braces). In some cases and generally in loops it makes more sense that variable has block scope.
Now, the variable i declared in second loop will have block scope of for loop. It will be available only in the one round of for loop. Now, the i is bound to every iteration of for loop. So, the new iteration will have seperate set of i here.
So, in second loop there will be 3 i which will bind to every iteration of for loop.
Now, when first iteration of for loop executes the value of i is 0 and it is bound to this iteration and setTimeout statement runs it is stored in background by javascript and will execute the function when 1s will be completed. But js waits for nobody. So next iterations of for loop will occur and in second iteration i value will be 1 bound to this iteration and so on.
When 1s happens the function of setTimeout will pushed to execution stack and each function have its i because for every iteration of for loop it has its own i which is bound to it(Here the concept of closures also come into picture).
So now every function execute of setTimeout and it will print 0,1 and 2.
Some more info
As told upward that i is bound to every iteration. So, when one iteration completed it will destroy. Then in next iteration how the for loop increemented 1 to the previous i. So, here the javascript actually uses var in background to maintain the for loop.
Any doubt or suggestion, make a comment