Почему не перечисляемые свойства не влияют на встроенные методы массива? - PullRequest
1 голос
/ 04 мая 2019

Я играл с методом Object.defineProperty () и свойством enumerable аргумента descriptor.На MDN вы можете прочитать следующее описание:

enumerable: true тогда и только тогда, когда это свойство обнаруживается при перечислении свойств соответствующего объекта.По умолчанию false.

Однако меня интересует причина, по которой он не влияет на методы, пересекающие итерируемые объекты, такие как for ... из .В следующем примере вы можете увидеть сравнение между обходом массива с использованием for ... of и 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;}

Кроме того, как упоминается в заголовке этого вопроса, built-in методы массива также игнорируют эту настройку, но object методы не:

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;}

Я не говорю, что это неправильное или неожиданное поведение, а просто ищу объяснение этой ситуации, спасибо!

1 Ответ

3 голосов
/ 04 мая 2019

Это из-за способа, которым указан итератор массива . Хотя чаще всего используются плотные массивы, в которых определен каждый ключ, также поддерживаются разреженные массивы, в которых определены лишь несколько ключей. Чтобы поддержать это, итерация массива должна иметь возможность повторять прошлые ключи, которые на самом деле не существуют. Например:

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);
}

Таким образом, по сути, итератор определен таким образом, что он перемещается от одного числового индекса к следующему, пока не достигнет длины массива. Попутно он не проверяет, являются ли эти индексы перечисляемыми или даже существуют ли они вообще.

For ... of использует итератор и, следовательно, зависит от него, как и некоторые методы массива. For ... in не использует итератор, и итераторы массива также не влияют на массивы (хотя они могут иметь свои собственные итераторы)

...