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, though, 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.
There is a difference of the scope with let and var, which causes them to behave differently.
Here is a quote from other Stack Overflow answer about the differences between var and let.
The main difference is scoping rules. Variables declared by var keyword are scoped to the immediate function body (hence the function scope) while let variables are scoped to the immediate enclosing block denoted by { } (hence the block scope).
So, in summary, 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.
An experiment that is possible is to make let behave like var. To make let behave like var, you can use (and run) the code below.
To see how let is behaving like var, read ahead about var hoisting!
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.
Thanks to Nick Vu for the experiment!
Here is a quick overview of var hoisting below.
The actual code (shown below):
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 how confusing var hoisting can be!
Always use let, unless var is absolutely needed!
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);
}
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.
Read
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. Here is how var i is constructed with hoisting under the hood.
var i; //hoisted
//`var` in `for` gets removed
for (i = 0; i < 3; i++) {
setTimeout(() => console.log(i), 1);
}
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`
}
Simply put, the setTimeout causes the log to run later, and using var has one instance of i, while using let has a new instance per loop.
Because of the event queue in JavaScript, the setTimeout callback function is called after the loop has been executed. Since the variable i in the first loop was declared using the var keyword, this value was global. During the loop, we incremented the value of i by 1 each time, using the unary operator ++. By the time the setTimeout callback function was invoked, i was equal to 3 in the first example.
In the second loop, the variable i was declared using the let keyword: variables declared with the let (and const) keyword are block-scoped (a block is anything between { }). During each iteration, i will have a new value, and each value is scoped inside the loop.
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.
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
This question already has answers here:
Explanation of `let` and block scoping with for loops
(5 answers)
Closed 5 years ago.
When let is used in the initialiser expression of a traditional for loop, it is scoped to the block of the for loop.
The spec says:
let and const declarations define variables that are scoped to the
running execution context's LexicalEnvironment.
Does that mean that the point of declaration of i is (and even in ES5 was(?)) lexically (semantically I know using var it would be hoisted) inside the block comprising the body of the for loop (because naively it looks like it is outside it).
Or does it mean that this is a new feature of let and/or the for loop to give it the semantics of being lexically inside the loop body?
To be clear: I know that the visibility semantics are new (i.e. block scoping for let vs function scooping for var). I am interested in whether the lexical position of definition has always been taken to be inside the body of the loop.
for(let i = 0; i < 10; i++) {
// the body of the loop....
}
Actually, think of it like an imaginary wrapper block with all the for-declared variables aliased within it. Kind of like this:
// Original
for (let i = 0; i < 10; i++) { ... }
// Equivalent
{
let _i;
for (_i = 0; _i < 10; _i++) {
let i = _i;
...
_i = i;
}
}
It works by creating a new scope within the for to alias the variables, and then effectively updating the aliases at the end of each block.
(Of course, engines implement it differently than this, but that's when you get into compiler theory and the difference between observable semantics and actual implementation.)
I have a simple loop which passes some data from a list through other functions, but when those functions return, the counter has mysteriously incremented:
for (i = 0; i < list.length; i++) {
alert(i) // ONE
doWhatever(list[i]);
alert(i) // TWO
}
doWhatever() looks like this:
function doWhatever (obj) {
for (i in obj) createThing(obj[i]);
}
How is it possible for the value of i in the original loop to change this way?
You seem to forget the var keyword.
Look at your code. And every where you have
for (i = 0; ...
change it to
for (var i = 0; ...
Because all the loops written without the var keyword change the same i variable (well, more precisely a loop written as such change the i of the enclosing scope having defined it, which is often the global one, especially if you never properly defined i).
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
JavaScript variables declare outside or inside loop?
So..I've seen many articles saying that we should use the following style.
var i;
for(i=0;i <= 10; i++) {
// do something here
}
I've been using the above style for while, but I just wonder if it really helps except the readability.
Isn't it same as the following?
for(var i=0; i<=10; i++) {
}
It makes a difference if for some reason (should never be the case) you've declared a global variable by the same name outside the context of the function.
http://jsfiddle.net/bFRKU/
var i = 'global';
function test(){
alert(i);
for(var i = 0; i < 10; i++){
//do something
}
}
test();
In the above example, you'll notice that the alert returns "undefined." This is because variable definitions are hoisted to the top of the function (no matter where they are declared within the function). So in reality, the above is interpreted as:
http://jsfiddle.net/bFRKU/1/
var i = 'global';
function test(){
var i;
alert(i);
for(i = 0; i < 10; i++){
//do something
}
}
test();
Thus the alert "undefined." Ultimately, the only reason to place your variable declarations at the top of your functions is to reduce this potential confusion.
No significant differences between the two -- purely a matter of opinion.
it's the same
It's done because in JS, the practice is to ensure that vars are declared in one spot, at the top of your function. Expressly because there is no block-scoping, and because of potential scope-chain resolution errors.
The error wouldn't come from declaring var, but rather, forgetting to, and relying on block-scope to have your back (which it doesn't, because it doesn't exist).
I'm sure I've read a discussion on SO about this but can't find it. Simply, are there cons to declaring a for loop's increment inside the loop's declaration? What is the difference between this:
function foo() {
for (var i=0; i<7; i++) {
// code
}
}
...and this:
function foo() {
var i;
for (i=0; i<7; i++) {
// code
}
}
Since JS has function scope, either should be fine, right? Are there edge cases where the former approach would cause problems?
If they are identical, why is Crockford/JSLint all, "No way dawg," about it?
These are exactly the same. All local variables in javascript have function scope which means they are alive for the entire function they are declared in. This is often counter intuitive at first as most curly brace languages scope the life time of the variable to the block they are declared in.
A portion of Javascript developers very much prefer the second form. The rationale is that since all variables have function scope, you should declare them at the function level to make the life time explicit even for those not familiar with Javascript. This is just a style though and by no means a hard rule
EDIT
Note that with the introduction of ES6 let, you can now use let inside your loop for real block-scoped variable more details
for(let i = 1; i <= 5; i++) {
setTimeout(function(){
console.log('Value of i : ' + i);
},100);
}
The problem with declaring with var in the loop header is that it's deceptive. It looks like you're declaring a variable whose scope is limited to the for loop, when it actually exists everywhere within the function - including before the declaration:
var i = 1;
function foo() {
console.log(i); // 'undefined'
for (var i=1; i<100; ++i) {
}
}
Even though the console.log call occurs before the declaration of the local i, it is still in scope for it because it's inside the same function. So the local i, which has not yet had any value assigned to it, is what gets passed to log. This can be surprising; it's certainly not obvious to anyone who's not familiar with Javascript scoping rules.
Starting with ECMAScript 2015, there is a better way to declare variables: let. Variables declared with let are local to the block containing them, not the entire function. So this version of the above code will print 1 as intended:
let i=1; // could use var here; no practical difference at outermost scope
function foo() {
console.log(i); // 1
for (let i=1; i<100; ++i) {
}
}
So best practice in modern Javascript is to declare variables with let instead of var. However, if you are stuck with a pre-ECMAScript 2015 implementation, it's a little less confusing to declare all variables at the top of the function, rather than waiting till first use.
There's no difference, but I prefer the second way (per Crockford) because it explicitly shows that the variable to available outside of the for loop:
function() {
for(var i=0; i<7; i++) {
// code
}
// i is still in scope here and has value 7
}
Those are both exactly the same thing.
The two code block are identical. The first statement of the for loop is executed before the for loop starts, the loop is running while the second statement is true, and the third statement is run every time the loop iterates once.
This means that
for(var i = 0; i < 8; i++) {
//some Code
}
is identical to
var i = 0;
for(;i < 8;) {
//some Code
i++;
}
(The semicolon following the ( is to tell the computer that i < 8 is actually the second statement, not the first).