Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't this simple for loop work as expected?

One might expect the following to print out a, b, c.

var i, rowName;
for (i = 0; i < 3; i++, rowName = ['a', 'b', 'c'][i]) {
    console.log(rowName);
}

Instead, however, it prints out undefined, b, c. Why?

To clarify: I know how to make this work; what I'm curious about is why the above doesn't work.

like image 212
Mark Avatar asked Nov 13 '15 01:11

Mark


2 Answers

The reason it prints undefined, b, c is because of how a for loop works.

for (initialization; condition; final expression)

Let's break down your for loop.

initialization: i = 0

condition: i < 3

final expression: i++, rowName = ['a', 'b', 'c'][i]

When your loop is first entered, i is set to 0. This is the initialization step. Then the condition step, i < 3, is checked. This is done before every iteration to decide whether or not to continue looping. After each loop, the final expression is evaluated. In your example, you increment i before setting rowName equal to an element in ['a', 'b', 'c'] based on the current index.

In your case, on the first iteration, rowName is undefined because the final expression is yet to be evaluated. Every iteration thereafter behaves as you would expect since a final expression has already been previously evaluated.

like image 197
royhowie Avatar answered Sep 22 '22 18:09

royhowie


If you want to do the tricky one-line for loop style, the "correct" syntax is:

var array = ['a', 'b', 'c'];
for (var i = 0, rowName; rowName = array[ i++ ]; ) {
    console.log(rowName);
}

Notice the ending ; of the for loop declaration. There's technically an empty statement after the ; which is where you normally would do i++.

In this case, the "condition" of the for loop is taking advantage of the Javascript assignment operator. If you have some code like this:

var a;
if( a = 1 ) { // Note this is assignment = not comparison ==
    console.log('true');
}

It will log "true". Why? Because inside of an expression, a = 1 actually returns 1. And 1 is "truthy," meaning it evaluates to true in a boolean context like an if statement.

The opposite is also true, if your value is falsey:

var a;
if( a = 0 ) {
    console.log('true');
}

It will not log, because a = 0 is returning 0 (as well as assigning 0 to a). And 0 is falsey.

This whacky for-loop syntax is only for certain conditions:

  • If any of the array elements are "falsey" (null, undefined, "", etc) it will prematurely terminate the loop, because of how operators work as mentioned above.
  • This assumes you don't care about the loop index i. It will be off by 1 for every iteration of the loop, because i++ is executed before the for block. That is, the first time your for body executes, i will be 1, not its declared starting value of 0.
  • The only benefit of this pattern is saving a few bytes. It's generally not used in the real world because of the above two pitfalls.
like image 32
Andy Ray Avatar answered Sep 22 '22 18:09

Andy Ray