JavaScript "new Array (n)" и "Array.prototype.map" странность

Я наблюдал это в 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)?

Ответ 1

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

x = new Array(3);

Создает массив с указателями undefined.

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

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

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

Ответ 2

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

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

Чтобы быстро создать такой массив:

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

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

Решение может состоять в том, чтобы заполнить элементы массива любым значением (даже с помощью undefined), используя 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));

Ответ 3

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

Ответ 4

Решение ES6:

[...Array(10)]

Не работает на typescript (2.3), хотя

Ответ 5

Массивы разные. Разница заключается в том, что 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.

Ответ 6

На странице MDC для map:

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

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

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

Ответ 7

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

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

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

Firefox просто "заполняет" эти пустые слоты с помощью undefined, даже если он не имеет значений.

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

Ответ 8

В спецификации 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);

Ответ 9

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

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

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

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

Ответ 10

Не ошибка. То, как определено конструктор массива, работает.

Из MDC:

Когда вы указываете один числовой параметр с помощью конструктора Array, вы указываете начальную длину массива. Следующий код создает массив из пяти элементов:

var billingMethod = new Array(5);

Поведение конструктора Array зависит от того, является ли единственный параметр числом.

Метод .map() включает только элементы итерации массива, которые явно присвоили значения. Даже явное присвоение undefined приведет к тому, что значение считается приемлемым для включения в итерацию. Это кажется странным, но по сути это разница между явным свойством undefined объекта и отсутствующим свойством:

var x = { }, y = { z: undefined };
if (x.z === y.z) // true

Объект x не имеет свойства, называемого "z", и объект y делает. Однако в обоих случаях оказывается, что "значение" свойства undefined. В массиве ситуация аналогична: значение length неявно выполняет присвоение значения всем элементам от нуля до length - 1. Таким образом, функция .map() не будет делать ничего (не будет вызывать обратный вызов) при вызове массива, недавно построенного с помощью конструктора Array и числового аргумента.

Ответ 11

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

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

Ответ 12

Вот простой способ утилиты в качестве обходного пути:

Простая карта для

function mapFor(toExclusive, callback) {
    callback = callback || function(){};
    var arr = [];
    for (var i = 0; i < toExclusive; i++) {
        arr.push(callback(i));
    }
    return arr;
};

var arr = mapFor(3, function(i){ return i; });
console.log(arr); // [0, 1, 2]
arr = mapFor(3);
console.log(arr); // [undefined, undefined, undefined]

Ответ 13

В Chrome, если я делаю new Array(3), я получаю [], поэтому я предполагаю, что вы столкнулись с ошибкой браузера.