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)
, я получаю []
, поэтому я предполагаю, что вы столкнулись с ошибкой браузера.