Смутно поведением `map` на массивах, созданных с использованием` new` - программирование
Подтвердить что ты не робот

Смутно поведением `map` на массивах, созданных с использованием` new`

Меня путают результаты map ping над массивом, созданным с помощью new:

function returnsFourteen() {
    return 14;
}

var a = new Array(4);
> [undefined x 4] in Chrome, [, , , ,] in Firefox
a.map(returnsFourteen);
> [undefined x 4] in Chrome, [, , , ,] in Firefox

var b = [undefined, undefined, undefined, undefined];
> [undefined, undefined, undefined, undefined]
b.map(returnsFourteen);
> [14, 14, 14, 14]

Я ожидал, что a.map(returnsFourteen) вернет [14, 14, 14, 14] (то же самое, что и b.map(returnsFourteen), потому что согласно странице MDN на массивах:

Если единственный аргумент, переданный конструктору Array, является целым числом между 0 и 2 ** 32-1 (включительно), создается новый массив JavaScript с таким количеством элементов.

Я интерпретирую это как означающее, что a должен иметь 4 элемента.

Что мне здесь не хватает?

4b9b3361

Ответ 1

Когда вы создаете такой массив:

var arr1 = new Array( 4 );

вы получаете массив с длиной 4, но у него нет элементов. Поэтому map не преобразовывает массив - массив не имеет элементов, которые нужно преобразовать.

С другой стороны, если вы это сделаете:

var arr2 = [ undefined, undefined, undefined, undefined ];

вы получаете и массив, который также имеет длину 4, но имеет 4 элемента.

Обратите внимание на разницу между отсутствием элементов и наличием элементов, значения которых undefined. К сожалению, выражение accessor свойства будет оценивать значение undefined в обоих случаях, поэтому:

arr1[0] // undefined
arr2[0] // undefined

Однако существует способ дифференцировать эти два массива:

'0' in arr1 // false
'0' in arr2 // true

Ответ 2

var a = new Array(4);

Это определяет новый объект массива с явной длиной 4, но без элементов.

var b = [undefined, undefined, undefined, undefined];

Это определяет новый объект массива с неявной длиной 4, с 4 элементами, каждый со значением undefined.

В docs:

callback вызывается только для индексов массива, которые назначены значения; он не вызывается для индексов, которые были удалены или которые никогда не были назначены значения.

Для массива a нет элементов, которым были присвоены значения, поэтому он ничего не делает.

Для массива b есть четыре элемента, которым были присвоены значения (да, undefined - значение), поэтому он отображает все четыре элемента в число 14.

Ответ 3

new Array(len) создает пустой массив и выполняет что-то другое, чем заполнение его значениями undefined: он устанавливает длину len. Итак, он переводит на этот код:

var newArr = [];
newArr.length = len;

Давайте немного поиграем с newArr (предположим, что len = 4):

newArr.length; //4
newArr[1] === undefined; //true
newArr.hasOwnProperty(1); //false

Это связано с тем, что в то время как 4 элемента длинные, он не содержит ни одного из этих 4 элементов. Представьте себе пустой пуля-клип: у него есть место для, скажем, 20 пуль, но оно не содержит ни одного из них. Они даже не были установлены в значение undefined, они просто... undefined (что немного запутывает.)

Теперь Array.prototype.map счастливо ходит по вашему первому массиву, щебетает и свистнет, и каждый раз, когда он видит элемент массива, он вызывает функцию на нем. Но, когда он идет вдоль пустого клипа, он не видит пуль. Конечно, есть место для пуль, но это не делает их существовавшими. Здесь нет значения, потому что ключ, который сопоставляется с этим значением, не существует.

Для второго массива, заполненного значениями undefined, значение равно undefined, и это также ключ. Внутри b[1] или b[3] есть что-то, но что-то не определено; но Array.prototype.map не волнует, он будет работать с любым значением, если у него есть ключ.

Для дальнейшего осмотра в спецификации:

Ответ 4

Еще один ответ на поведение console.log. Простой, выход сахара и технически неправильный.

Давайте рассмотрим этот пример:

var foo = new Array(4),
    bar = [undefined, undefined, undefined, undefined];

console.log( Object.getOwnPropertyNames(bar) );
console.log( Object.getOwnPropertyNames(foo) );

Как мы видим в результате, функция .getOwnPropertyNames возвращает

["length"]

для массива foo Array/Object, но

["length", "0", "1", "2", "3"]

для массива bar Array/Object.

Таким образом, console.log просто обманывает вас при выводе Arrays, у которого есть только определенный .length, но нет реальных назначений свойств.