Почему мне нужно скопировать массив, чтобы использовать метод для него?

Я могу использовать Array() чтобы получить массив с фиксированным числом неопределенных записей. Например

Array(2); // [empty × 2] 

Но если я пойду и использую метод map, скажем, в моем новом массиве, записи все еще не определены:

Array(2).map( () => "foo");  // [empty × 2] 

Если я скопирую массив, карта работает:

[...Array(2)].map( () => "foo");  // ["foo", "foo"]

Зачем мне нужна копия для использования массива?

Ответы

Ответ 1

При использовании Array(arrayLength), чтобы создать массив, вы будете иметь:

новый массив JavaScript со свойством length, установленным на это число (Примечание: это подразумевает массив пустых слотов arrayLength, а не слотов с фактическими неопределенными значениями).

Массив на самом деле не содержит никаких значений, даже не undefined значений - он просто имеет свойство length.

Когда вы распространяете элемент со свойством length в массив, например [...{ length: 4 }], синтаксис распространения получает доступ к каждому индексу и устанавливает значение этого индекса в новом массиве. Например:

const arr1 = [];
arr1.length = 4;
// arr1 does not actually have any index properties:
console.log('1' in arr1);

const arr2 = [...arr1];
console.log(arr2);
console.log('2' in arr2);

Ответ 2

Причина в том, что элемент массива не назначен. Смотрите здесь первый абзац описания.... обратный вызов вызывается только для индексов массива, которым присвоены значения, в том числе неопределенные.

Рассматривать:

var array1 = Array(2);
array1[0] = undefined;

// pass a function to map
const map1 = array1.map(x => x * 2);

console.log(array1);
console.log(map1);

Выходы:

Array [undefined, undefined]
Array [NaN, undefined]

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

Операция отображения вызывает операцию отображения для первого элемента, поскольку она была определена (посредством назначения). Он не вызывает операцию сопоставления для второго аргумента, а просто теряет значение undefined.

Ответ 3

Как указывает @CertainPerformance, ваш массив не имеет никаких свойств, кроме его length, вы можете проверить это с помощью следующей строки: new Array(1).hasOwnProperty(0), которая возвращает false.

Посмотрев на 15.4.4.19 Array.prototype.map, вы можете увидеть, на 7.3 есть проверка, существует ли ключ в массиве.

1..6. [...]
7. Повторите, пока k < len

  1. Пусть Pk будет ToString (k).

  2. Пусть kPresent будет результатом вызова внутреннего метода [[HasProperty]] для O с аргументом Pk.

  3. Если kPresent верно, то
    1. Пусть kValue будет результатом вызова внутреннего метода [[Get]] для O с аргументом Pk.

    2. Пусть mappedValue будет результатом вызова внутреннего метода [[Call]] для callbackfn с T в качестве значения this и списком аргументов, содержащим kValue, k и O.

    3. Вызвать внутренний метод [[DefineOwnProperty]] объекта A с аргументами Pk, дескриптор свойства {[[Value]]: mappedValue, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true } и ложь.

  4. Увеличьте k на 1.

9. Верните А.

Ответ 4

Как уже указывалось, Array(2) будет создавать только массив из двух пустых слотов, которые не могут быть отображены.

Что касается some и every, Array(2) действительно пустой массив:

Array(2).some(() => 1 > 0); //=> false
Array(2).every(() => 1 < 0); //=> true

Однако этот массив пустых слотов можно каким-то образом "повторить":

[].join(','); //=> ''
Array(2).join(','); //=> ','

JSON.stringify([]) //=> '[]'
JSON.stringify(Array(2)) //=> '[null,null]'

Таким образом, мы можем использовать эти знания, чтобы придумать интересные и несколько ироничные способы использования функций массива ES5 в таких "пустых" массивах.

Вы сами придумали это:

[...Array(2)].map(foo);

Вариант предложения от @charlietfl:

Array(2).fill().map(foo);

Лично я бы порекомендовал это:

Array.from(Array(2)).map(foo);

Немного старая школа:

Array.apply(null, Array(2)).map(foo);

Этот довольно многословный:

Array(2).join(',').split(',').map(foo);