I was playing with the method Object.defineProperty() and the enumerable property of the descriptor argument. On the MDN you can read the next description:

enumerable: true if and only if this property shows up during enumeration of the properties on the corresponding object. Defaults to false.

However, I'm wonder about the reason it don't affect methods that traverse iterable objects, like the for ... of. On the next example you can see the comparison between traversing the array using for ... of and for ... in.

let arr = [1, 2, 3, 4, 5];
Object.defineProperty(arr, "4", {value: 99, enumerable: false});

console.log("For ... of traverse non-enumerable properties:");

for (const ele of arr)
{
    console.log(ele);
}

console.log("For ... in don't traverse non-enumerable properties:");

for (const key in arr)
{
    console.log(arr[key]);
}
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

Also, as the title of this question mention, built-in array methods ignores this setting too, but object methods don't:

let arr = [1, 2, 3, 4, 5];
Object.defineProperty(arr, "4", {value: 99, enumerable: false});

console.log("forEach(): ");
arr.forEach(x => console.log(x));

console.log("map(): ", arr.map(x => x + 1));

console.log("reduce(): ", arr.reduce((acc, x) => `${acc + x},` , ""));

console.log("Object.keys(): ", Object.keys(arr));

console.log("Object.values(): ", Object.values(arr));
.as-console {background-color:black !important; color:lime;}
.as-console-wrapper {max-height:100% !important; top:0;}

I'm not saying this is wrong or unexpected behavior, but just looking for an explanation of this situation, thanks!

1 Answers

3
Nicholas Tower On Best Solutions

It's because of the way the array iterator is specified. While it's most common to have dense arrays where every key is defined, sparse arrays are also supported, where only a handful of keys are defined. To support this, array iteration needs to be able to iterate past keys that don't actually exist. For example:

const arr = [];
arr[0] = 0;
arr[10] = 10;

console.log('has 0?', arr.hasOwnProperty(0))
console.log('has 1?', arr.hasOwnProperty(1))

for (let val of arr) {
  console.log(val);
}

So basically, the iterator is defined in such a way that it moves from one numerical index to the next until it reaches the length of the array. Along the way, it does not check whether those indexes are enumerable, or even if they exist at all.

For ... of uses the iterator and thus is affected by this, as are some array methods. For ... in does not use the iterator, and non-arrays are also not affected by the array iterator (though they may have their own iterators)