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).
Related
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.
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
a = [];
for (var i = 0; i < 3; i++) {
a.push(function() {
console.log(i);
})
}
a[0]() // I want 0, but I get 3
I am trying to write a simple piece of code where I have an array of functions such that when I execute a function at a particular index, the index value should get printed.
However, the piece of code above shows the same result (3 in this case) for all index values. I understand that the value is pointing by reference and therefore points to the last value of i. Could someone point out how to do this in the right manner?
Wrap it around a function. Now each time the loop executes, the wrapper function has its own value of i.
a = [];
for (var i = 0; i < 3; i++) {
(function(i){
a.push(function() {
console.log(i);
})
})(i);
}
a[0]()
You can add a self executing function to act like a module. Doing this, the scope of the variable i is in that function.
a = [];
for (var i = 0; i < 3; i++) {
(function(i){
a.push(function() {
alert(i);
})
})(i);
}
a[0]()
Note: In this block (function(i){ ... })(i), i can have any name, there is no connection between i from loop and i from function, i.e. (function(r){ ... })(r).
Here is an alternate version to an anonymous function that gets created and executed all at once.
The issue that you are having is that when the function gets called, the loop has already been evaluated and the value of i has already reached the max value of 3. You need trap the current value of i while the loop is being evaluated.
var a = [];
for (var i = 0; i < 3; i++) {
var fn = function() {
console.log(arguments.callee.i);
}
fn.i = i; // Pass as 'i' parameter to 'fn'.
a.push(fn);
}
a[0](); // The value 0 will be printed, rather than 3.
There is more than one way to skin a cat. The code above is very similar to:
var a = [];
function fn(i) {
return function() {
console.log(i);
}
}
for (var i = 0; i < 3; i++) {
a.push(fn(i));
}
a[0](); // The value 0 will be printed, rather than 3.
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
This is the regular for-loop:
for (var i = 0; i < n; i++) { ... }
It is used to iterate over arrays, but also to just repeat some process n times.
I use the above mentioned form, but it repulses me. The header var i = 0; i < n; i++ is plain ugly and has to be rewritten literally every time it is used.
I am writing this question because I came up with an alternative:
repeat(n, function(i) { ... });
Here we use the repeat function which takes two arguments:
1. the number of iterations,
2. a function which body represents the process that is being repeated.
The "code-behind" would be like so:
function repeat(n, f) {
for (var i = 0; i < n; i++) {
f(i);
}
}
(I am aware of the performance implications of having two additional "levels" in the scope chain of the process)
BTW, for those of you who use the jQuery library, the above mentioned functionality can be achieved out-of-the-box via the $.each method like so:
$.each(Array(n), function(i) { ... });
So what do you think? Is this repeat function a valid alternative to the native for loop? What are the down-sides of this alternative (other than performance - I know about that)?
Native:
for (var i = 0; i < 10; i++) {
// do stuff
}
Alternative:
repeat(10, function(i) {
// do stuff
});
You say you want a revolution... Well, you know: ruby did it just before (?)
Number.prototype.times = function(func) {
for(var i = 0; i < Number(this); i++) {
func(i);
}
}
means
(50).times(function(i) {
console.log(i)
})
Anyway, don't fight against C, you'll always lose :-P
it's an interesting thought, but if you dislike the syntax for the loop, you could always do a different type of loop:
var i = arr.length;
while (i--) {
// do stuff
}
the reverse while loop is generally faster than a for loop as well.
To address the issue of not having the break statement as others have mentioned, I would solve it this way:
function repeat(n, f) {
for (var i = 0; i < n; i++) {
if (f(i) === false) return;
}
}
Then returning false from within a loop handler will be equivalent to break.
Another disadvantage is that the context changes. You may want to add the option of proxying a context into the loop handlers:
function repeat(context, n, f) {
if (!f) f = n, f = context, context = window;
for (var i = 0; i < n; i++) {
if (f.call(context, i) === false) return;
}
}
Now, an advantage is that the index is preserved by the function scope, to avoid a common bug:
for (var i = 0; i < 10; i++) {
setTimeout(function () {
alert(i); // Will alert "10" every time
}, 1000);
}
repeat(10, function (i) {
setTimeout(function() {
alert(i); // Will alert "0", "1", "2", ...
}, 1000);
});
It seems pretty valid. I honestly don't think that performance would decrease too much. But there is however one big downside, that is easily fixable: the break statement.
function repeat(n, f) {
for (var i = 0; i < n; i++) {
var tcall=i;
tcall.die=function(){i=n}
f.call(tcall);
}
}
This way you would be able to call this.die() instead of break; which I think would throw an error.
Besides what you have already stated the main downside I see is that a "return" statement will work differently. (Which is often why I end up using "for" over "$.each" many times in my own ventures.)