Here is a quotation from MDN about 'for' statement :
An expression (including assignment expressions) or variable
declaration. Typically used to initialize a counter variable. This
expression may optionally declare new variables with the var keyword.
These variables are not local to the loop, i.e. they are in the same
scope the for loop is in. The result of this expression is discarded.
So when I write this kind of code :
for(var i = 0; i < 10; i++) {
for(var j = 0; j < 10; j++) {
// ...
}
}
At each iteration of the outer loop I declare the variable j, which was already declared right ?
So is it better to write something like this :
for(var i = 0, j = 0; i < 10; i++) {
for(j = 0; j < 10; j++) {
// ...
}
}
... or we do not care?
The behavior this quote refers to is called hoisting and is important to know in JavaScript.
Here's how the MDN explains it:
Because variable declarations (and declarations in general) are
processed before any code is executed, declaring a variable anywhere
in the code is equivalent to declaring it at the top. This also means
that a variable can appear to be used before it's declared. This
behavior is called "hoisting", as it appears that the variable
declaration is moved to the top of the function or global code.
The reason you don't declare all your variables at the top of the function, is because their localization makes the code clearer.
There's absolutely no gain in having both vars being declared in the first loop. It's only confusing. It's the same for the JS engine, but the other developers reading this code will wonder why j is declared at an unexpected location.
Now, if you're disturbed by the fact your variable exists before (with an undefined value) and after the loop where you use it, rejoice: there's a new declaration type coming with ES6: let, which scopes the variable to the block.
for(let i = 0; i < 10; i++) {
for(let j = 0; j < 10; j++) {
// ...
}
}
Beware: compatibility of let
For now, please use the standard form that everybody expects:
for(var i = 0; i < 10; i++) {
for(var j = 0; j < 10; j++) {
// ...
}
}
The convention, in such a case, is that i and j won't be used outside of
their loop. When (and only in this case) you want to use i or j after (for example there's a break in the loop), use this:
var i, j;
for(i = 0; i < 10; i++) {
for(j = 0; j < 10; j++) {
// ...
}
}
// use i and j here
Related
I am trying to detect nested loop with the same index which looks like this:
for(let i = 0; i < 10; i++) {
for(let i = 0; i < 10; i++) {
}
}
I have searched Eslint rules but haven't found any solution so far. Is there a way to detect this bad code smell? Thanks a lot.
ESLint has the no-shadow rule that would flag that up, as well as other places where you've shadowed an outer scope variable with an inner scope one. For example:
{
"no-shadow": ["error", { "builtinGlobals": false, "hoist": "functions", "allow": [] }]
}
Demo on the ESLint site:
for(let i = 0; i < 10; i++) {
for(let i = 0; i < 10; i++) {
// ^−−−−− 'i' is already declared in the upper scope on line 1 column 9.
console.log(i);
}
}
You can't do it, since it's pure logic
I see examples like this online:
const roles = [];
for (i of roles) {
roleObj[roles[i].key] = true;
}
do we need not declare the variable i, like so?
for (let i of roles) {
roleObj[roles[i].key] = true;
}
tons of articles are promoting the first example, which seems pretty dumb to me:
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
You don't absolutely need to, but it's highly recommended to do so and in fact to use let. Note that using let i in this case will actually function differently than if you just did for (i or for (var i.
As an example:
for (let i = 0; i < 10; i++) {
process.nextTick(() => console.log(i));
}
// print 0..9
for (var i = 0; i < 10; i++) {
process.nextTick(() => console.log(i));
}
// prints 10 ten times.
Also note that with let you would not be able to use i after the loop, but you could with var, and if you do not use var the variable will be in the global scope so it would work differently if it were inside of a function:
function gl() {
for (i = 0; i < 10; i++) {}
for (var j = 0; i < 10; i++) {}
for (let x = 0; i < 10; i++) {}
console.log(i, j) // prints 10, 10
console.log(x) // runtime error
}
gl();
console.log(i) // prints 10
console.log(j) // runtime error
Also, as mentioned in the comments, accessing variables without declarations is not allowed in strict mode.
I run the two following code snippets.
The first loop is giving the expected result when I assign a different name for the local variable.
for(var i = 0; i < 3; i++) {
setTimeout((function() {
var i2 = i;//named i2 here
return function(){console.log(i2)};
})(), 10);
}
The second loop will print undefined instead. I thought
var i = i will just re-declare the original i. And I expect it pint out some number.
How come I get undefined here?
for(var i = 0; i < 3; i++) {
setTimeout((function() {
var i = i;
console.log(i);
return function(){console.log(i)};
})(), 10);
}
The scope of the initialization expression of var is the body of the function, and the local variable i is already in that scope. So you can't reference an outer variable with the same name. You can think of
var x = <expression>;
as equivalent to:
var x;
x = <expression>;
If you look at it this way, you can see why var i = i; won't work, it's equivalent to:
var i;
i = i;
The assignment uses the uninitialized value of the local variable.
The usual idiom to solve this is to make i a parameter to the function, which you then pass in the argument list of the IIFE.
for(var i = 0; i < 3; i++) {
setTimeout((function(i) {
console.log(i);
return function(){console.log(i)};
})(i), 10);
}
For more information, see JavaScript closure inside loops – simple practical example
Scope
The inner var i declares a new variable called i within a scope that already has a variable called i.
The declaration is assigned the value of the scope's most prominent i, which is the one you just declared.
Since the value of the newly declared i is undefined, the assignation of itself to itself is of its value of undefined.
The variable i is already being used by the loop for in the outer scope. Now you are declaring a new variable i in an inner scope
var i = i;
Once that statement is ran the inner scope is the counter i of the loop is more accessible because you overridden it with new i.
Basically what you are doing here is: defining a new variable i and assign to it a value of that variable you just declared which is undefined.
Eaiset solution is to declare j and assign to it the value i.
for(var i = 0; i < 3; i++) {
setTimeout((function() {
var j = i;
console.log(j);
return function(){console.log(j)};
})(), 10);
}
OR just use i while passing it the function setTimeout
for(var i = 0; i < 3; i++) {
setTimeout((function(i) {
console.log(i);
return function(){console.log(i)};
})(), 10);
}
My guess is it should be return function(){console.log(i)}; in the second snippet. And the reason it still does not work is, that you're basically assigning to var i the same i you just declared with var. You'll see what I mean if you split declaration and assignment:
var i;
i = i;
But yeah, I can see how js quirks like that can be quite frustrating, because one has to always watch out and reinvent new variable names as one goes down the call stack. This is one reason why I fell in love with TypeScript, because it compiles this:
for (let i = 0; i < 3; ++i) {
window.setTimeout(() => {
console.log(i);
});
}
into this:
var _loop_1 = function (i) {
window.setTimeout(function () {
console.log(i);
});
};
for (var i = 0; i < 3; ++i) {
_loop_1(i);
}
which will yield the expected result.
To begin with, I apologize if this question has been asked previously. I have searched for something similar to what I'm asking here and have found nothing but dead ends.
I am reviewing some older code for a project and I am using Brackets as my IDE with the JSLint extension. While going through my code it recommended that I change a line similar to
for(var i = 0; i < somevalue; i++)
to
var i;
for(i = 0; i < somevalue; i++)
This prompted me to ask; is there is any significant difference between the two declarations from a performance aspect, coding standard aspect, or etc.?
Thank you for any answers or leads!
JSLint is actually asking you to move the variable declaration to the top of the function scope:
function myfunction() {
var i,
j,
k;
// other code
for (i = 0; i < 100; i=i+1) { //another JSLint Recommendation: Don't use ++
for (j = 0; j < 100; j=j+1) {
for (k = 0; k < 100; k=k+1) {
console.log(i);
console.log(j);
console.log(k);
}
}
}
}
The reason is that variables have function level scope in JavaScript. If I had declared 'j' inside of the for loop for i, it would have been 'hoisted' to the top of the function and would have actually existed throughout that whole function, not just in the for loop.
JSLint makes this recommendation because that's what is going on behind the scenes anyway, and you could be in for a rude surprise if you don't expect that to be the case.
Consider following 2 functions
function test1(){
//code block 1
for(var i = 0; i < somevalue; i++)
//code block 2
}
function test2(){
//code block 1
var i;
for(i = 0; i < somevalue; i++)
//code block 2
}
In both cases the definitions are hoisted and the compiler first defines the variables. The compiler rearranges the code like this,
function test(){
var i; //and other definitions
//code block 1(without definitions)
for(i = 0; i < somevalue; i++)
//code block 2(without definitions)
}
Therefore there is no difference..
var i binds the variable i to the local context. If you don't do var i, the variable is defined globally.
you should always use var in only one place, at least this is the best practice. Also you should use it per variable only once
so
for(var i = 0;i<10;i++)
is fine but
for(var i = 0;i<10;i++)
for(var i = 0;i<10;i++)
is not good better would be
for(var i = 0;i<10;i++)
for(i = 0;i<10;i++)
but if you do
var i;
for(i = 0;i<10;i++)
or not is totaly up to you
As we know, javascript has no BLOCK SCOPE, so when I wrote a for loop like below:
list = [1, 2, 3, 4];
// notice about the `var` keyword
for(var i = 0; i < list.length; ++i) {
// Do something.
}
console.log(i); // shows 4
The matter is: should I use var to declare the variable i?
IF SO:
When I have two or more consecutive for loop, I want to use the same cursor variable i, it will be declare more than once! That simply have problem!
for(var i = 0; i < list1.length; ++i) {
// do something.
}
for(var i = 0; i < list2.length; ++i) {
// do something.
}
// `i` was declared more than once!
In this form, the cursor variable i maybe declared more than once, and the code itself implies that the i variable is likely to have a scope inside the for block, but indeed NOT.
IF NOT SO:
Should I explicitly declare all for cursors earlier in the function?
var i, j, k; // and maybe a long list that I didn't expected?
// Maybe some other code.
for(i = 0; i < count1; ++i) {
// do something
}
for(j = 0; j < count2; ++j) {
// do something
}
for(k = 0; k < count3; ++k) {
// do something
}
If I code this way, I think the code is terrible: it has a long distance between the declaration and use, and obscure about what they are when declared.
If I omit the declarations for them all, and just use, these cursor variables falls into globals! That's much more terrible!
So I'm asking for a good practice, how to declare the cursor variable in this case?
Need your help.
Typically it's simplest to declare the variable, then use it in multiple non-nested loops.
var i;
for(i = 0; i < list1.length; i++) {
// do something.
}
for(i = 0; i < list2.length; i++) {
// do something.
}
There is no problem with reusing i in multiple loops. As soon as the second loop starts, the value is set to the initial value and everything is fine.
Declaring a variable hoists it to the top of the function and successive declarations are syntactically legal but ignored. So:
var i, j, k; // and maybe a long list that I didn't expected?
// Maybe some other code.
for(i = 0; i < count1; ++i) {
// do something
}
...
is what the interpreter does under the hood if you declared it multiple times.
Declaring it in this way (variables on the top) is therefore usually what people suggest (and what JSLint suggests).