When do for loops cause type coercion javascript?

Viewed 78

When I do this:

const foo = [0, 1, 2]

for (i = 0; i < foo.length; i ++){
    console.log(foo[i], typeof foo[i])
}

I get this:

0 number
1 number
2 number

But when I do this:

for (let item in foo){
    console.log(item, typeof item)
}

I get this:

0 string
1 string
2 string

What is going on here?? What rules would I have needed to know to predict this?

Edit: I am running Node 12.18.3

2 Answers

There's no coercion going on here. You are actually iterating two different primitive types with your two different loops.

Traditional for loops with counters, count with numbers.

for/in loops are for iterating over properties of objects. Objects have keys/properties, which are always strings. So when you are seeing 0, 1, and 2, you are seeing the strings "0", "1", and "2".

So, when you use a for/in loop on an array instance, it's enumerated by the keys of the Array object, which include the string representation of the array indexes as well as any enumerable properties of the Array instance. You can then wind up enumerating other properties of the array besides the items in it as shown here:

const foo = [0, 1, 2];

// Add a property to the array instance
foo.bar = "baz";

for (let item in foo){
  // This will log ALL the enumerable properties
  // of the object, in this case, including properties
  // that are not the array values.
  console.log(item, foo[item]);
}

Here's another way to see this. Below is an object literal being enumerated with a for/in. Note that when the literal is created the keys don't get quotes around them, yet when their type is tested (they key name, not the key value), they are reported as strings, because they implicitly are:

let obj = {
 key1: "something",
 key2: "something else",
 foo: 42,
 false: true,
 99: 100
};

for(let prop in obj){
  // Note that even the unquoted key name of 
  // false is implicitly a string, not a Boolean
  // and the 99 key is a string, not a number
  console.log(prop, "(" + typeof prop + ")", obj[prop], "(" + typeof obj[prop] + ")");
}

As stated in the docs (link above):

The for...in statement iterates over all enumerable properties of an object that are keyed by strings (ignoring ones keyed by Symbols), including inherited enumerable properties.

You basically mixes the values of the array (first loop when you call foo[i] in the classic for) and keys / indexes of the array (when you call for ... in ....

Just try with foo = [5,6,7] and you will clearly see the difference.

for (let a in [5,6,7])
  console.log(a, typeof a)

As stated in the other answer from Scott Marcus, index are like keys and are string here.

Also note that if you want loop over all elements of an array (or enumerable), use for ... of ...:

for (let a of [5,6,7])
  console.log(a, typeof a)

Related