107

What's the correct way to write a for-in loop in JavaScript? The browser doesn't issue a complaint about either of the two approaches I show here. First, there is this approach where the iteration variable x is explicitly declared:

for (var x in set) {
    ...
}

And alternatively this approach which reads more naturally but doesn't seem correct to me:

for (x in set) {
    ...
}
DavidRR
  • 18,291
  • 25
  • 109
  • 191
futlib
  • 8,258
  • 13
  • 40
  • 55
  • Just came across this post when troubleshooting why a webpack generated bundle file was causing errors at a for loop where `var` was not used to declare the iterator `i`: `Uncaught ReferenceError: i is not defined`. So I will be using it from now on :/ webpack treats "global" variables weirdly, for more see: https://stackoverflow.com/a/40416826 – user1063287 May 24 '18 at 12:28

10 Answers10

111

Use var, it reduces the scope of the variable otherwise the variable looks up to the nearest closure searching for a var statement. If it cannot find a var then it is global (if you are in a strict mode, using strict, global variables throw an error). This can lead to problems like the following.

function f (){
    for (i=0; i<5; i++);
}
var i = 2;
f ();
alert (i); //i == 5. i should be 2

If you write var i in the for loop the alert shows 2.

JavaScript Scoping and Hoisting

Gabriel Llamas
  • 18,244
  • 26
  • 87
  • 112
  • 4
    Does not answer the question, this is for normal for loop, not for for in. – IS4 Oct 29 '12 at 18:48
  • Isn't the reason i==5 due more to hoisting than the lack of var in the for loop? – Snekse Apr 19 '13 at 13:36
  • 1
    Another importnat aspect to this is that strict mode forbids implicit creation of gobal properties, so using the standard "for in" loop w/o the var statement will actually fail and return a ReferenceError. – dkugappi Jun 11 '13 at 15:01
  • 2
    But, coming from Java, putting the `var` inside the `for` head looks like it's local in the for loop, which it isn't. Hence, I prefer user422039's style below. – njlarsson Oct 23 '14 at 12:04
  • 2
    What if you happen to have more than one for-loop in one scope? You will either have to reuse the index (no var), or you will have to declare lots and lots of new variables (j, k, l, m, …) which you will never use again. – armin Sep 02 '15 at 05:14
  • @armin, in the case of multiple for loops, I would declare `var i;` before the for loops so it is clear how you are using it (See user422039 answer below: http://stackoverflow.com/a/5717575/922522). – Justin Nov 03 '15 at 20:30
  • @Justin Makes sense. Also makes sense, if you just started coding something and you don't know how many nested loops you will need. And you may also want to reuse the variable in a not-nested loop. So at the end of the day you could say that the var will **not** be declared inside the for statement, right? – armin Nov 07 '15 at 20:13
  • This is a bit misleading as declaring `var` inside **this** `for` loop limits the scope of `i` to `f` but it doesn't limit the scope to the `for` loop. See: https://jsbin.com/wopozaxuzo/edit?js,output – Assimilater Sep 08 '16 at 01:55
41

The first version:

for (var x in set) {
    ...
}

declares a local variable called x. The second version:

for (x in set) {
    ...
}

does not.

If x is already a local variable (i.e. you have a var x; or var x = ...; somewhere earlier in your current scope (i.e. the current function)) then they will be equivalent. If x is not already a local variable, then using the second will implicitly declare a global variable x. Consider this code:

var obj1 = {hey: 10, there: 15};
var obj2 = {heli: 99, copter: 10};
function loop1() {
    for (x in obj1) alert(x);
}
function loop2() {
    for (x in obj2) {
        loop1(); 
        alert(x);
    }
}
loop2();

you might expect this to alert hey, there, heli, hey, there, copter, but since the x is one and the same it will alert hey, there, there, hey, there, there. You don't want that! Use var x in your for loops.

To top it all off: if the for loop is in the global scope (i.e. not in a function), then the local scope (the scope x is declared in if you use var x) is the same as the global scope (the scope x is implicitly declared in if you use x without a var), so the two versions will be identical.

Claudiu
  • 224,032
  • 165
  • 485
  • 680
  • 4
    Finally a complete answer with explanation and nice example. And it really answers the question. – IS4 Oct 29 '12 at 18:48
26

You really should declare local variables with var, always.

You also should not use "for ... in" loops unless you're absolutely sure that that's what you want to do. For iterating through real arrays (which is pretty common), you should always use a loop with a numeric index:

for (var i = 0; i < array.length; ++i) {
  var element = array[i];
  // ...
}

Iterating through a plain array with "for ... in" can have unexpected consequences, because your loop may pick up attributes of the array besides the numerically indexed ones.

edit — here in 2015 it's also fine to use .forEach() to iterate through an array:

array.forEach(function(arrayElement, index, array) {
  // first parameter is an element of the array
  // second parameter is the index of the element in the array
  // third parameter is the array itself
  ...
});

The .forEach() method is present on the Array prototype from IE9 forward.

Pointy
  • 405,095
  • 59
  • 585
  • 614
  • It might be well to make your use of the Array `.forEach()` method more clear with regard to your distinguishing between the Object for/in loop versus the Array for/length loop. – Thomas Jan 26 '21 at 18:28
12

Actually, if you dislike declaration within for heading, you can do:

var x;
for (x in set) {
    ...
}

As mentioned in other answers to this question, not using var at all produces unnecessary side-effects like assigning a global property.

DavidRR
  • 18,291
  • 25
  • 109
  • 191
user422039
  • 1,447
  • 4
  • 13
  • 28
9
for(var i = 0; ...)

is a commonly seen pattern but it's different from

for(int i; ...)

in C++ in that that the variable isn't scoped to the for block. In fact, the var gets hoisted to the top of the enclosing scope (function) so a local i will be effectively available both before the for loop (after the beginning of the current scope/function) and after it.

In other words, doing:

(function(){ //beginning of your current scope;
 //...
 for(var i in obj) { ... };
})();

is the same as:

(function(){ //beginning of your current scope;
 var i;
 //...
 for(i in obj) { ... };
})();

ES6 has the let keyword (instead of var) to limit the scope to the for block.

Of course, you SHOULD be using local variables (ones declared with either var or let or const (in ES6)) rather than implicit globals.

for(i=0; ...) or for(i in ...) will fail if you use "use strict"; (as you should) and i isn't declared.

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
9

Use the one where you declare the loop variable with var. Implicitly declared variables have a different scope that's probably not what you intended.

Joel Coehoorn
  • 399,467
  • 113
  • 570
  • 794
6

Using var is the cleanest way, but both work as described here: https://developer.mozilla.org/en/JavaScript/Reference/Statements/for...in

Basically, by using var you ensure that you create a new variable. Otherwise you might accidentally use a previously defined variable.

DavidRR
  • 18,291
  • 25
  • 109
  • 191
TJHeuvel
  • 12,403
  • 4
  • 37
  • 46
4

I think var is good for performance reasons.

Javascript won't look through the whole global scope to see if x already exists somewhere else.

neebz
  • 11,465
  • 7
  • 47
  • 64
3

From a general point of view, first version will be for an index that must live within loop's scope, while the other one would be any variable in the scope where loop's constructor got invoked.

If you're going to use loop's index inside for loop and this won't be required by others in next lines, better declare the variable with "var" so you'll be sure "x" is for loop's index initialized with 0, while the other one, if other "x" variable is available in this context, this will get overwritten by loop's index - that's you'll have some logical errors -.

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206
0

I always use the block scoped let introduced in ES2015.

for (let x in set) {
    ...
}

Additional reading and examples

user3071284
  • 6,955
  • 6
  • 43
  • 57