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).
Related
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
I am interested how for loop operate behind the scenes in ES6.
This is the basic example
var funcs = [];
for (let i = 0; i < 5; i++) {
funcs.push(function () {
console.log(i);
});
};
The reason each function will get proper i value (from 0 to 4) is because let is creating 5 new scopes and functions are bound to their corresponding scope. Thats what I think and that makes most sense to me. If that is the case then it doesnt make sense why const declaration (i) is failing since it should be creating 5 new scopes and const variables can live happily in different scopes.
Before let we have do use IIFE in order to achieve the same effect, but what that code did is that it basically created new scopes for the functions and I thought let is doing the same behind the scenes.
If the above statement is not correct, then it must be that let inside for loop is only creating one scope but then I do not get how is that different from var declaration and how functions get proper i value. To make it even more clear, lets say that let is bound to one new scope which is created by the for loop and var declaration is hoisted to the global scope in this case but thats still one scope to be working with.
Can anyone share some light on this topic?
The reason each function will get proper i value (from 0 to 4) is because let is creating 5 new scopes and functions are bound to their corresponding scope.
Yes, this is what happens. See Explanation of `let` and block scoping with for loops for details (actually there are 6 scopes).
If that is the case then it doesnt make sense why const declaration (i) is failing since it should be creating 5 new scopes and const variables can live happily in different scopes.
Your false assumption here is that const works the same as let. Yes, it would totally be possible for 5 consts to be created in the loop body evaluations. But that just doesn't make sense for a loop like
for (const i=0; i<5; i++) {
console.log(i);
}
which would lead to a TypeError: Assignment to constant variable - you cannot increment a const. So you shouldn't use it in a for loop (notice that for…in and for…of loops are different again).
OK, one could write something like
let i=0; for (const j=i; i<5; i++) …
and expect it to work and get 5 different const j variables in the body. But that's not what happens either, as that's a totally weird way to write this. If you want a const declaration in each loop iteration, better write it explicitly:
for (let i=0; i<5; i++) {
const j=i;
…
}
which is clean and clear and actually does what you might have expected above.
How const declarations in for loops are actually meant to be used can be shown in this example:
for (const iterator = {…}; iterator.hasNext(); iterator.step()) {
… iterator.getValue() …
}
If we check the spec, this actually has the same effect as
{
const iterator = {…};
for (; iterator.hasNext(); iterator.step()) {
… iterator.getValue() …
}
}
which means that the constant is declared once in the head of the loop. It doesn't make sense to re-declare it in every iteration, after all its value is constant anyway.
This question already has answers here:
Can I declare the same variable twice in different for loops in JavaScript? [duplicate]
(2 answers)
Closed 7 years ago.
In javascript variables have function scope only. So it's either global or it's a variable accessible in the entire function regardless of exactly where it was declared (within the function).
I'm guessing best practice would be to declare all variables at the top level right after 'use strict'; ?
What happens if I have 5 loops where I declare the same i var?
for (var i = 0; i < someValue; i+=1) { ... }
Is the variable simply reset to 0 or whatever the loop sets it's initial value and everything moves on ? Can this cause complications ?
As you can see, i is set to 0 at the start of the for loop: i = 0. The var i part is hoisted to the top of the function and every additional var i … redeclares i, and the i = 0 part redefines it. Redeclaration is unnecessary and makes the code less clean (JSHint warns you about this).
If you use i later, outside of any loop, that i is set to whatever the for loop did with it.
for(var i = 0; i < 4; i++){
/*
As the last few steps i is incremented (i == 4),
then it is checked with (i < 4), which is false; the loop is done.
*/
//…
}
i; // 4
Because you’d normally redefine every iteration variable in the head of the loop, this cannot cause any other problems.
However, for cleaner code it’s wise to use one variable per loop, or to make let variables, instead of var variables, as let variables are scoped to the for loop block only:
for(let i = 0; i < 20; i++){
// …
}
for(let i = 0; i < 10; i++){ // This is fine.
// …
}
let variables are only available in ECMAScript-6-compliant browsers and in Firefox require a version opt-in.
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).
Consider this code:
for (var j = 0; j < 10; j++){
// ...
}
Assume that a for loop appears right after this one, which uses the same j counter variable. Netbeans complains that the second j redefines the first one, because of variable hoisting.
Is there any reason why or why not I should surround my loop with a closure:
(function(){
for (var j = 0; j < 10; j++){
// ...
}
})();
... to prevent the variable hoisting behavior, and stop Netbean's complaints?
Javascript variables have function scope - not the block scope you are probably used to. (This is a big Javascript wart that we must get used to)
The usual style rules suggest that it is better is to declare all variables, including loop variables just once in the beginning of the function. (Don't declare them inside the for statement - it makes sense in other languages but not in Javascript).
Hopefully this causes your compiler to calm down.
(You don't need to worry about both loops using the same variable that much - the variable is reinitialized anyway)
Is it complaining because you are redeclaring var? You can remove the second var since it would already be in scope.
The problem is that javascript variables are not block scoped but function scoped
Suppose you have a snippet like this
function MyDemo(){
var i = 0;
for( var j = 0; j < 10; j++ ){
++i;
}
alert( i );
alert( j );
}
In javascript j will be alerted as 10, while as in a block scoped language like C# or Java, it would be something like j not defined in the context
That being the problem, the solution is not to wrap it in a closure.
Why don't you use another variable or re-initialise the same variable without declaring again?
Useful Link: Explaining JavaScript Scope And Closures
Wrapping the loop in a closure seems unnecessary to me and could potentially cause issues if you're defining variables inside the for loop that you would like you use outside of the loop. For example:
(function() {
for (var j = 0; j < 10; j++) {
var foo = "bar"
}
});
// foo is undefined here
Whereas:
for (var j = 0; j < 10; j++) {
var foo = "bar"
}
// foo is defined here
From a micro-performance standpoint, the closure also creates unnecessary overhead since, as other people have said, if you take the var off of the second for loop, it should stop complaining.
99% of the time, the runtime performance hit of the extra function call is going to be irrelevant. What's more important is the extra code cluttering up your codebase and making things harder to read. I'd say don't wrap that in a function call just for your IDE's sake.