Hidden Quirks of JavaScript `for...in` Loops

The C-style three-expression for loop in JavaScript is often viewed as old school or awkward beginner style. Some developers automatically use a for...of, foreach, or for...in statement instead. But the latter has a subtle pitfall that can lead to unexpected behaviour.

Upper part of a ferris-wheel at night
Photo by Yunus Emre on Unsplash

Why are My List Indices Suddenly Strings post?

This little piece of JavaScript code is supposed to convert a 0-based list of indices into a 1-based list:

const zeroBased = ['foo', 'bar', 'baz'];

for (const i in zeroBased) {
    console.log(`${i + 1}`);
}

But the output may not be what you expect:

01
11
21

It becomes clearer, if you slightly change the code:

const zeroBased = ['foo', 'bar', 'baz'];

for (const i in zeroBased) {
    console.log(`${i + 1} (${typeof i})`);
}

Output

01 (string)
11 (string)
21 (string)

It looks like all indices have been converted to strings and the + operator concatenates instead of adding numerically.

But this explanation is not quite correct. In fact, no index gets converted to a string. Arrays in JavaScript are just objects and object keys are always strings. You can even mix string keys with numerical keys in JavaScript:

const arr = ['foo', 'bar', 'baz'];
arr['four'] = 'huh?';
console.log(arr['four']); // Output 'huh?'.
console.log(Array.isArray(arr)); // Output `true`.
console.log(arr); // Output `[ 'foo', 'bar', 'baz', four: 'huh?' ]`

In reality, JavaScript arrays differ from objects only in that they have additional methods like map(), filter(), and so on. The differences look bigger than they are because functions like JSON.stringify(), console.log(), console.dir() and friends serialize or visualize arrays and non-array-objects differently.

Many times, the subtle problems stemming from this go unnoticed because code that ignores them just works as intended. Take this example:

const zeroBased = ['foo', 'bar', 'baz'];

for (const i in zeroBased) {
    console.log(zeroBased[i]);
}

Output

foo
bar
baz

The variable i is still a string. But when used as an Array index, it is automatically coerced into a number.

Conclusion

If you want to access array indices, use the classic 3-expression form for loop! It may be less concise but it is more explicit. And it is more efficient if you need the indices as numbers because it saves you from converting the strings back into number with parseInt() which is ugly and a waste of CPU cycles.

Leave a comment

Abusing JSON.stringify()

Hidden Quirks of JavaScript `for...in` Loops

Creating E-Invoices with Free and Open Source Software

Dynamic Angular Configuration

Compiling ImageMagick for Perl

Standalone Angular Tour Of Heroes

This website uses cookies and similar technologies to provide certain features, enhance the user experience and deliver content that is relevant to your interests. Depending on their purpose, analysis and marketing cookies may be used in addition to technically necessary cookies. By clicking on "Agree and continue", you declare your consent to the use of the aforementioned cookies. Here you can make detailed settings or revoke your consent (in part if necessary) with effect for the future. For further information, please refer to our Privacy Policy.