JavaScript "new Array (n)" и "Array.prototype.map" странность - PullRequest
179 голосов
/ 31 марта 2011

Я наблюдал это в Firefox-3.5.7 / Firebug-1.5.3 и Firefox-3.6.16 / Firebug-1.6.2

Когда я запускаю Firebug:

    >>> x = new Array(3)
    [undefined, undefined, undefined]
    >>> y = [undefined, undefined, undefined]
    [undefined, undefined, undefined]

    >>> x.constructor == y.constructor
    true

    >>> x.map(function(){ return 0; })
    [undefined, undefined, undefined]
    >>> y.map(function(){ return 0; })
    [0, 0, 0]

Что здесь происходит? Это ошибка, или я неправильно понимаю, как использовать new Array(3)?

Ответы [ 13 ]

112 голосов
/ 31 марта 2011

Похоже, что первый пример

x = new Array(3);

Создает массив с неопределенными указателями.

А второй создает массив с указателями на 3 неопределенных объекта, в этом случае сами указатели НЕ являются неопределенными, только те объекты, на которые они указывают.

y = [undefined, undefined, undefined]
// The following is not equivalent to the above, it's the same as new Array(3)
y = [,,,];

Поскольку карта запускается в контексте объектов в массиве, я считаю, что первая карта вообще не запускает функцию, в то время как вторая удается запустить.

90 голосов
/ 29 января 2016

У меня была задача, что я знал только длину массива и нуждался в преобразовании элементов. Я хотел сделать что-то вроде этого:

let arr = new Array(10).map((val,idx) => idx);

Чтобы быстро создать массив следующим образом:

[0,1,2,3,4,5,6,7,8,9]

Но это не сработало, потому что: см. ответ Джонатана Лоновски на несколько ответов вверху.

Решением может быть заполнение элементов массива любым значением (даже с неопределенным), используя Array.prototype.fill ()

let arr = new Array(10).fill(undefined).map((val,idx) => idx);

console.log(new Array(10).fill(undefined).map((val, idx) => idx));

Обновление

Другое решение может быть:

let arr = Array.apply(null, Array(10)).map((val, idx) => idx);

console.log(Array.apply(null, Array(10)).map((val, idx) => idx));
72 голосов
/ 05 октября 2016

С ES6 вы можете сделать [...Array(10)].map((a, b) => a), быстро и просто!

20 голосов
/ 05 мая 2017

ES6 решение:

[...Array(10)]

Не работает на машинописи (2.3), хотя

17 голосов
/ 31 марта 2011

Массивы разные.Разница в том, что new Array(3) создает массив длиной три, но без свойств, в то время как [undefined, undefined, undefined] создает массив длиной три и три свойства с именами "0", "1" и "2", каждый из которых имеетзначение undefined.Вы можете увидеть разницу, используя оператор in:

"0" in new Array(3); // false
"0" in [undefined, undefined, undefined]; // true

Это связано с немного запутанным фактом, что если вы пытаетесь получить значение несуществующего свойства любого нативного объекта в JavaScript,возвращает undefined (вместо того, чтобы выдавать ошибку, как это происходит, когда вы пытаетесь сослаться на несуществующую переменную), что совпадает с тем, что вы получаете, если ранее для свойства было явно установлено значение undefined.

14 голосов
/ 31 марта 2011

Со страницы MDC для map:

[...] callback вызывается только для индексов массива, которым присвоено значение;[...]

[undefined] фактически применяет установщик к индексу (-ам), так что map будет повторяться, тогда как new Array(1) просто инициализирует индекс (-ы) значением по умолчаниюиз undefined, поэтому map пропускает его.

Я считаю, что это одинаково для всех методов итерации .

7 голосов
/ 31 марта 2011

Я думаю, что лучший способ объяснить это - посмотреть, как Chrome справляется с этим.

>>> x = new Array(3)
[]
>>> x.length
3

Так что на самом деле происходит то, что new Array () возвращает пустой массив, имеющий длину 3, но без значений. Поэтому, когда вы запускаете x.map на технически пустом массиве, нечего настраивать.

Firefox просто «заполняет» эти пустые слоты undefined, даже если у него нет значений.

Я не думаю, что это явно ошибка, просто плохой способ представить, что происходит. Я полагаю, что Chrome "более корректен", поскольку показывает, что в массиве на самом деле ничего нет.

6 голосов
/ 18 апреля 2017

В спецификации ECMAScript 6-е издание.

new Array(3) определяет только свойство length и не определяет свойства индекса, такие как {length: 3}.см. https://www.ecma -international.org / ecma-262 / 6.0 / index.html # sec-array-len Шаг 9.

[undefined, undefined, undefined] определит свойства индекса и свойство длины как{0: undefined, 1: undefined, 2: undefined, length: 3}.см https://www.ecma -international.org / ecma-262 / 6.0 / index.html # sec-runtime-semantics-arrayaccumulation ElementList Шаг 5.

методы map, every, some, forEach, slice, reduce, reduceRight, filter массива будут проверять свойство индекса внутренним методом HasProperty, поэтому new Array(3).map(v => 1) не будет вызывать обратный вызов.

для получения более подробной информации см. https://www.ecma -international.org / ecma-262 / 6.0 / index.html # sec-array.prototype.map

Какисправить?

let a = new Array(3);
a.join('.').split('.').map(v => 1);

let a = new Array(3);
a.fill(1);

let a = new Array(3);
a.fill(undefined).map(v => 1);

let a = new Array(3);
[...a].map(v => 1);
4 голосов
/ 29 октября 2013

Просто столкнулся с этим. Конечно, было бы удобно использовать Array(n).map.

Array(3) дает примерно {length: 3}

[undefined, undefined, undefined] создает пронумерованные свойства:
{0: undefined, 1: undefined, 2: undefined, length: 3}.

Реализация map () действует только на определенные свойства.

3 голосов
/ 21 марта 2016

Если вы делаете это для того, чтобы легко заполнить массив значениями, вы не можете использовать fill по соображениям поддержки браузера и действительно не хотите делать цикл for, вы также можетесделайте x = new Array(3).join(".").split(".").map(..., который даст вам массив пустых строк.

Довольно некрасиво сказать, но, по крайней мере, проблема и намерение достаточно четко сообщены.

...