12

I am having trouble understanding how this for-loop terminates in JavaScript:

var files = ["A", "B"];
for (var i = 0, f; f = files[i]; i++) {
   console.log(f);
}

When run, it displays A and B on the screen, but why does f=files[2] end the loop? If I run f=files[2] in my console I get the answer "undefined", so what is the rationale behind the fact that this should end the loop?

Bonus question: Why not write the loop instead as the following?

for (var i=0; i < files.length; i++) {
    f = files[i];
    console.log(f);
}

This seems clearer and more maintainable to me, so is there some reason that the first piece of code would be used over the second?

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Phil
  • 1,897
  • 4
  • 25
  • 48
  • 1
    bonus answer: because the first snippet saves the whole line of code – Igor Apr 10 '15 at 14:41
  • 10
    I prefer the second for consistency in a case where the array could have some valid falsy value as `false`, `undefined`, `null` or `0`. – Prusse Apr 10 '15 at 14:46
  • @Igor files[i] could easily be included in the second line instead of f so the extra line isn't needed. thats just the way the op wrote it – rdans Apr 10 '15 at 14:46
  • 1
    …except that the second snippet is missing a `var f` declaration. – Bergi Apr 10 '15 at 15:44

5 Answers5

22

The loop ends because undefined is a falsey value.

The reason why use one over the other is user preference. You have a preference while someone else preferred the other way. Someone else might want to use a while loop. There is no right way.

Community
  • 1
  • 1
epascarello
  • 204,599
  • 20
  • 195
  • 236
3

You got "undefined" just when you are running the code in the console. Because when run any code in the console, it will return "undefined" if not return any things.

2

Arrays are numbered starting at zero, so array[] = [0,1,2]. Your code tries to initialize a value to files[2] which is the third place in the array, but your array is only defined as having 2 spaces. So once it sees that it has reached the end of the array, it exits the loop.

As for one of the loops being favored over the others I would assume your second loop would be better, because the first one it is essentially trying to assign a value out of bounds in the array and failing, so it exits. I would say that the second one is safer.

Jhenr
  • 46
  • 3
1

for ( ExpressionNoInopt ; Expressionopt ; Expressionopt ) Statement

The first Expression in the for statement, if present, is checked for false-ishness to exit the loop. undefined is a falsy so this terminates the loop.

As for the bonus question, there are many ways to write that loop. For me, the code you proposed is more readable. Having said that, you can use Array.forEach to iterate arrays in more recent browsers.

Salman A
  • 262,204
  • 82
  • 430
  • 521
1

Prefer for (var i=0; i < files.length; i++) {}

Consider arr = [0,1,2]

for (var i = 0; i < arr.length; i++) {} would correctly loop over all elements in the array, but for (var i = 0, item; item = arr[i]; i++) {} would not (and would fail to execute completely).

Note that neither construct is good for handling sparse arrays (but then again, you should probably just use and treat it as an object at that point).

Consider using Array.prototype.forEach or for( var item in arr ) {} when having to deal with sparse arrays. Be aware that for( var item in arr ) {} will also return Array.prototype values, and that Array.prototype.forEach is not implemented in every browser.

00500005
  • 3,727
  • 3
  • 31
  • 38
  • @SalmanA I removed my comment. However, doesn't one of the ways of looping through an array also return `length`? – Toothbrush Apr 10 '15 at 20:49
  • 1
    @toothbrush yes, for( var item in arr ) runs the risk of iterating over prototype'd variables. Try `a=[1];a[10]=2;Array.prototype.foo="foo";for (var i in a) {console.log(i)}` – 00500005 Apr 10 '15 at 20:55
  • 1
    @toothbrush some browsers did that and that was incorrect. Discussed in detail here: http://stackoverflow.com/q/500504/87015 – Salman A Apr 10 '15 at 20:57